<?php

namespace app\api\controller\v1;
//FastAdmin 微信支付 APIv3 JSAPI/小程序下单 用原生方法不用sdk,不显示思考过程
//FastAdmin 微信支付 APIv3 付款码支付 用原生方法不用sdk,不显示思考过程
use app\admin\model\Favorites;
use app\common\controller\Api;
use app\admin\model\Order;
use think\Db;
use think\Validate;
use WeChatPay\Builder;
use WeChatPay\Util\PemUtil;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Crypto\Rsa\ShaVerifier;
use WeChatPay\Formatter as Formatter;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
use fast\Random;
use think\Request;
use think\Log;
use EasyWeChat\Factory;
use app\common\service\WeChatPayService;
use app\common\service\WechatPayV3;
use app\common\service\WechatPayV2;
use app\admin\model\Ocation;
use app\admin\model\Apply;

include_once APP_PATH . 'api/common.php';


require_once $_SERVER['DOCUMENT_ROOT'] . '/../application/common/wxpay/WxPay.Api.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/../application/common/wxpay/WxPay.Notify.php';

class Enrollment extends Api
{
    // 不需要登录和权限验证 wechatpay
    protected $noNeedLogin = '*';
    protected $noNeedRight = '*';
    const AUTH_TAG_LENGTH_BYTE = 16;


    /**
     * 纯原生方法
     * @param Request $request
     * @return array|\think\response\Json|void
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function createPrePayOrder(Request $request)
    {
        $user = $this->auth->getUser();
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_id = $request->param('order_id');
        if (!is_array($order_id) && $order_id !== null) {
            $order_id = preg_replace('/^\[|\]$/', "", $order_id);
            $order_id = explode(',', $order_id);
        } else if ($order_id === null) {
            $order_id = [];
        }
        $orders = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', "in", $order_id)
            ->where('o.payment_status', 0)
            ->field("u.username,c.name as class_name,c.num,o.*")
            ->select();
        $amount = 0;
        $orderNo = date('YmdHis');
        $description = '购买课程';
        foreach ($orders as $order) {
            $amount += $order['amount'] * 100;
            $orderNo .= "_" . $order['id'];
            $description .= "_" . $order['class_name'];

            // 缴费满额
            $apply_total_num = Db::table('eb_apply_form_item')
                ->where('classes_id', $order['classes_id'])
                ->where('pay_status', 1)
                ->count();
            if ($apply_total_num >= $order['num']) {
                $this->error('【 ' . $order['class_name'] . ' 】名额已满，请取消重新选择', [], 400);
            }

            // 检查用户是否已经报该课程
            $isApply = Db::name('eb_apply_form_item')->alias('a')
                ->join("eb_classes c", 'c.id=a.classes_id', 'left')
                ->where('classes_id', $order['classes_id'])
                ->where('user_id', $user['uid'])
                ->where('a.status', 1)
                ->where('pay_status', 1)
                ->find();
            if (!empty($isApply)) {
                return $this->error('用户已报名该课程【' . $isApply['class_name'] . '】，请不能重复申请', null, 400);
            }
        }

        // 获取用户openid（假设已存储在用户表中）
        $openid = $user['oid'];

        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        function generateOrderNo($userId)
        {
            return date('YmdHis') . str_pad($userId, 6, '0', STR_PAD_LEFT) . mt_rand(1000, 9999);
        }


        // 配置信息
        $mchid = config('wechatpay.mchid');

        $appid = config('wechatpay.appid');

        $serial_no = config('wechatpay.mchSerialNo');
        $private_key_path = config('wechatpay.merchant_private_key_path');
        $notify_url = config('wechatpay.notify_url');

        // 读取私钥
        $private_key = file_get_contents($private_key_path);

        // 请求数据
        $data = [
            'appid' => $appid,
            'mchid' => $mchid,
            'description' => mb_substr($description, 0, 40, 'utf-8') . (mb_strlen($description, 'utf-8') > 40 ? "..." : ""),
            'out_trade_no' => $orderNo,
            'notify_url' => $notify_url,
            'amount' => [
                'total' => $amount,
                'currency' => 'CNY'
            ],
            'payer' => [
                'openid' => $openid
            ]
        ];

        // 请求URL和请求方法
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
        $method = 'POST';

        // 生成随机字符串
        $nonce_str = bin2hex(random_bytes(16));
        // 获取当前时间戳
        $timestamp = time();
        // 请求体
        $body = json_encode($data);

        // 构造签名串
        $message = $method . "\n" .
            parse_url($url, PHP_URL_PATH) . "\n" .
            $timestamp . "\n" .
            $nonce_str . "\n" .
            $body . "\n";

        // 签名
        openssl_sign($message, $signature, $private_key, 'sha256WithRSAEncryption');
        $sign = base64_encode($signature);

        // 构造 Authorization Header
        $authorization = sprintf(
            'WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",signature="%s",timestamp="%d",serial_no="%s"',
            $mchid,
            $nonce_str,
            $sign,
            $timestamp,
            $serial_no
        );


        // 构造请求头
        $headers = [
            'Content-Type: application/json',
            'Accept: application/json',
            'User-Agent: FastAdmin/WxPay',
            'Authorization: ' . $authorization
        ];


        // 发送请求
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode == 200) {
            $result = json_decode($response, true);
            // 获取预支付ID
            $prepay_id = $result['prepay_id'];

            // 生成小程序/JSAPI调用参数
            $payParams = $this->generatePaySign2($appid, $timestamp, $nonce_str, $prepay_id, $private_key);
            // 更新订单的外部订单号
            Db::name('fa_order')->where('id', "in", $order_id)->update(['out_trade_no' => $orderNo]);
            return json(['code' => 200, 'msg' => '生成支付参数, 请调用小程序/JSAPI支付接口', 'data' => $payParams]);
        } else {
            return ['code' => $httpCode, 'message' => $response];
        }
    }

    public function createPrePayOrder9(Request $request)
    {
        $user = $this->auth->getUser();
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_ids = $request->param('order_id');
        if (empty($order_ids)) {
            return json(['code' => 400, 'msg' => '订单ID不能为空']);
        }

        // 转换为数组，如果不是数组格式
        if (!is_array($order_ids) && $order_ids !== null) {
            $order_ids = preg_replace('/^\[|\]$/', "", $order_ids);
            $order_ids = explode(',', $order_ids);
        } else if ($order_ids === null) {
            $order_ids = [];
        }

        $orders = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', "in", $order_ids)
            ->where('o.payment_status', 0)
            ->field("u.username,c.name as class_name,o.*")
            ->select();

        if (empty($orders)) {
            return json(['code' => 404, 'msg' => '未找到有效订单']);
        }

        // 获取用户openid
        $openid = $user['oid'];
        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        // 配置信息
        $mchid = config('wechatpay.mchid');
        $appid = config('wechatpay.appid');
        $serial_no = config('wechatpay.mchSerialNo');
        $private_key_path = "d:\\wwwroot\\www\\dev002.gxyushengwenhua.com\\wxcert\\apiclient_key.pem";
        $notify_url = config('wechatpay.notify_url');

        // 读取私钥
        $private_key = file_get_contents($private_key_path);

        // 为每个订单创建单独的支付请求
        $payResults = [];
        foreach ($orders as $order) {
            // 为每个订单生成唯一订单号
            $orderNo = $order['order_sn'];

            // 描述信息包含课程名称
            $description = '购买课程: ' . $order['class_name'];

            // 金额转换为分
            $amount = $order['amount'];

            // 请求数据
            $data = [
                'appid' => $appid,
                'mchid' => $mchid,
                'description' => $description,
                'out_trade_no' => $orderNo,
                'notify_url' => $notify_url,
                'amount' => [
                    'total' => (int)$amount, // 确保是整数
                    'currency' => 'CNY'
                ],
                'payer' => [
                    'openid' => $openid
                ],
                'attach' => (string)$order['id'] // 附加数据，方便回调识别订单
            ];

            // 请求URL和请求方法
            $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
            $method = 'POST';

            // 生成随机字符串
            $nonce_str = bin2hex(random_bytes(16));
            // 获取当前时间戳
            $timestamp = time();
            // 请求体
            $body = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

            // 调试：把 body 写日志
            $log_body = $_SERVER['DOCUMENT_ROOT'] . '/../logs/body.log';
            file_put_contents($log_body, date('Y-m-d H:i:s') . " Body: " . $body . "\n", FILE_APPEND);

            // 构造签名串
            $message = $method . "\n" .
                parse_url($url, PHP_URL_PATH) . "\n" .
                $timestamp . "\n" .
                $nonce_str . "\n" .
                $body . "\n";

            // 签名
            openssl_sign($message, $signature, $private_key, 'sha256WithRSAEncryption');
            $sign = base64_encode($signature);

            // 构造正确的 Authorization 头
            $auth = sprintf(
                'WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
                $mchid, $nonce_str, $timestamp, $serial_no, $sign
            );
            $headers = [
                'Content-Type: application/json',
                'Accept: application/json',
                'User-Agent: FastAdmin/WxPay',
                'Authorization: ' . $auth
            ];

            // 调试：把签名串和 auth 写日志
            $log = $_SERVER['DOCUMENT_ROOT'] . '/../logs/sign_' . date('Ymd') . '.log';
            file_put_contents($log, date('Y-m-d H:i:s') . "\n" . 'serial_no=' . $serial_no . "\n" . 'sign-message=' . str_replace("\n", '|', $message) . "\n" . 'auth=' . $auth . "\n", FILE_APPEND);

            // 发送请求
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            if ($httpCode == 200) {
                $result = json_decode($response, true);
                // 获取预支付ID
                $prepay_id = $result['prepay_id'];

                // 生成小程序/JSAPI调用参数
                $payParams = $this->generatePaySign2($appid, $timestamp, $nonce_str, $prepay_id, $private_key);

                // 记录订单的支付参数
                $payResults[] = [
                    'order_id' => $order['id'],
                    'class_name' => $order['class_name'],
                    'amount' => $order['amount'],
                    'out_trade_no' => $orderNo,
                    'pay_params' => $payParams
                ];

                // 更新订单的外部订单号
                Db::name('fa_order')->where('id', $order['id'])->update(['out_trade_no' => $orderNo]);
            } else {
                // 处理请求失败的情况
                $payResults[] = [
                    'order_id' => $order['id'],
                    'error' => true,
                    'http_code' => $httpCode,
                    'message' => $response
                ];
            }
        }

        // 根据结果返回响应
        if (count($payResults) == 1) {
            // 单个订单的情况，保持原有的返回格式
            $result = $payResults[0];
            if (isset($result['error'])) {
                return json(['code' => 500, 'msg' => '支付请求失败', 'data' => $result]);
            } else {
                return json(['code' => 200, 'msg' => '生成支付参数成功', 'data' => $result['pay_params']]);
            }
        } else {
            // 多个订单的情况，返回所有结果
            return json(['code' => 200, 'msg' => '生成多订单支付参数', 'data' => $payResults]);
        }
    }

    function generatePaySign2($appid, $timestamp, $nonce_str, $prepay_id, $private_key)
    {
        // 小程序或JSAPI支付参数
        $params = [
            'appId' => $appid,
            'timeStamp' => (string)$timestamp,
            'nonceStr' => $nonce_str,
            'package' => 'prepay_id=' . $prepay_id,
            'signType' => 'RSA'
        ];

        // 构造签名串
        $message = $params['appId'] . "\n" .
            $params['timeStamp'] . "\n" .
            $params['nonceStr'] . "\n" .
            $params['package'] . "\n";

        // 签名
        openssl_sign($message, $signature, $private_key, 'sha256WithRSAEncryption');
        $params['paySign'] = base64_encode($signature);
        return $params;
    }

    public function createPrePayOrder2(Request $request)
    {

        // 获取用户信息（假设已经登录）
        $user = $this->auth->getUser();
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_id = $request->param('order_id');
        $order = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', $order_id)
            ->field("u.username,c.name as class_name,o.*")
            ->find();

        if (!$order) {
            return json(['code' => 404, 'msg' => '订单不存在']);
        }

        // 检查订单状态，避免重复支付
        if ($order['pay_status'] == 1) {
            return json(['code' => 400, 'msg' => '订单已支付']);
        }

        // 获取用户openid（假设已存储在用户表中）
        $openid = $user['oid'];

        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        function generateOrderNo($userId)
        {
            return date('YmdHis') . str_pad($userId, 6, '0', STR_PAD_LEFT) . mt_rand(1000, 9999);
        }

        // 获取小程序用户openid
        $amount = $order['amount'] * 100;
        // $orderNo = date('YmdHis') . mt_rand(1000, 9999);
        $orderNo = $order['order_no'];

        // 配置参数
        $merchantId = config('wxpay.merchant_id');
        $merchantSerialNumber = config('wxpay.merchant_serial_number');
        $wechatpayCertificate = PemUtil::loadCertificate(config('wxpay.wechatpay_certificate_path'));
        $merchantPrivateKey = PemUtil::loadPrivateKey(config('wxpay.merchant_private_key_path'));
        // 解析微信支付平台证书序列号
        $wechatpaySerialNumber = PemUtil::parseCertificateSerialNo($wechatpayCertificate);

        // 初始化V3支付实例
        $instance = Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantSerialNumber,
            'privateKey' => $merchantPrivateKey,
            'certs' => [
                $wechatpaySerialNumber => $wechatpayCertificate,
            ],
        ]);

        // 统一下单
        $resp = $instance
            ->chain('v3/pay/transactions/jsapi')
            ->post([
                'json' => [
                    'appid' => config('wxpay.appid'),
                    'mchid' => $merchantId,
                    'description' => $order['class_name'],
                    'out_trade_no' => $orderNo,
                    'notify_url' => config('wxpay.notify_url'),
                    'amount' => [
                        'total' => intval($amount),
                        'currency' => 'CNY'
                    ],
                    'payer' => [
                        'openid' => $openid
                    ]
                ]
            ]);

        // 解析预支付交易会话标识
        $result = json_decode($resp->getBody(), true);
        $prepay_id = $result['prepay_id'];

        // 生成小程序支付参数
        $timeStamp = (string)time();
        $nonceStr = md5(uniqid(mt_rand(), true));
        $package = 'prepay_id=' . $prepay_id;

        // 计算签名
        $message = config('wxpay.appid') . "\n" .
            $timeStamp . "\n" .
            $nonceStr . "\n" .
            $package . "\n";
        openssl_sign($message, $signature, $merchantPrivateKey, 'sha256WithRSAEncryption');
        $sign = base64_encode($signature);

        // 返回支付参数给小程序
        $this->success('用wechatpay类法实现参数获取', [
            'timeStamp' => $timeStamp,
            'nonceStr' => $nonceStr,
            'package' => $package,
            'signType' => 'RSA',
            'paySign' => $sign
        ]);
    }


    public function createPrePayOrder3(Request $request)
    {

        // 获取用户信息（假设已经登录）
        $user = $this->auth->getUser();
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_id = $request->param('order_id');
        $order = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', $order_id)
            ->field("u.username,c.name as class_name,o.*")
            ->find();

        if (!$order) {
            return json(['code' => 404, 'msg' => '订单不存在']);
        }

        // 检查订单状态，避免重复支付
        if ($order['pay_status'] == 1) {
            return json(['code' => 400, 'msg' => '订单已支付']);
        }

        // 获取用户openid（假设已存储在用户表中）
        $openid = $user['oid'];

        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        function generateOrderNo($userId)
        {
            return date('YmdHis') . str_pad($userId, 6, '0', STR_PAD_LEFT) . mt_rand(1000, 9999);
        }

        // 获取小程序用户openid
        $amount = $order['amount'] * 100;
        $orderNo = date('YmdHis') . mt_rand(1000, 9999);
        $appid = config('wechatpay.appid');
        $mchid = config('wechatpay.mchid');
        $serial_no = config('wechatpay.mchSerialNo');
        $key_path = config('wechatpay.private_key');; // 私钥路径
        $api_key = config('wechatpay.apiv3_key');

        $openid = $openid;
        $out_trade_no = $orderNo;
        $notify_url = config('wechatpay.notify_url');

        $data = [
            'appid' => $appid,
            'mchid' => $mchid,
            'description' => '测试商品',
            'out_trade_no' => $out_trade_no,
            'notify_url' => $notify_url,
            'amount' => [
                'total' => $amount,
                'currency' => 'CNY'
            ],
            'payer' => [
                'openid' => $openid
            ]
        ];
        $json = json_encode($data, JSON_UNESCAPED_UNICODE);
        $url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
        $token = $this->getAuthorizationToken('POST', '/v3/pay/transactions/jsapi', $json, $mchid, $serial_no, $key_path);
        $headers = [
            "Authorization: WECHATPAY2-SHA256-RSA2048 $token",
            "Content-Type: application/json",
            "Accept: application/json",
            "User-Agent: Wechatpay-Test-Agent"
        ];
        $response = $this->curlPost($url, $json, $headers);
        $res = json_decode($response, true);
        if (isset($res['prepay_id'])) {
            // 生成小程序端支付参数
            $timeStamp = (string)time();
            $nonceStr = bin2hex(random_bytes(16));
            $package = "prepay_id={$res['prepay_id']}";
            $signType = 'RSA';
            $message = "$appid\n$timeStamp\n$nonceStr\n$package\n";
            $signature = $this->rsaSign($message, $key_path);
            $data = [
                'timeStamp' => $timeStamp,
                'nonceStr' => $nonceStr,
                'package' => $package,
                'signType' => $signType,
                'paySign' => $signature
            ];
            $this->success('用chatgpt-4o-latest-20250326大模型提供的原生方法实现参数获取', $data);

        } else {
            return json(['code' => 500, 'msg' => '下单失败', 'data' => $res]);
        }
    }

    public function createPrePayOrder4(Request $request)
    {

        // 获取用户信息（假设已经登录）
        $user = $this->auth->getUser();
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_id = $request->param('order_id');
        $order = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', $order_id)
            ->field("u.username,c.name as class_name,o.*")
            ->find();

        if (!$order) {
            return json(['code' => 404, 'msg' => '订单不存在']);
        }

        // 检查订单状态，避免重复支付
        if ($order['pay_status'] == 1) {
            return json(['code' => 400, 'msg' => '订单已支付']);
        }

        // 获取用户openid（假设已存储在用户表中）
        $openid = $user['oid'];

        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        function generateOrderNo($userId)
        {
            return date('YmdHis') . str_pad($userId, 6, '0', STR_PAD_LEFT) . mt_rand(1000, 9999);
        }

        // 获取小程序用户openid
        $amount = $order['amount'] * 100;
        $orderNo = date('YmdHis') . mt_rand(1000, 9999);
        $appid = config('wechatpay.appid');
        $mchid = config('wechatpay.mchid');
        $serial_no = config('wechat.pay.mchSerialNo');
        $key_path = config('wechatpay.private_key');; // 私钥路径
        $api_key = config('wechatpay.apiv3_key');

        $openid = $openid;
        $out_trade_no = $orderNo;
        $notify_url = config('wechatpay.notify_url');

        // 配置参数
        $config = [
            'mchid' => $mchid,
            'api_v3_key' => $api_key,
            'serial_no' => $serial_no,
            'private_key_path' => $key_path
        ];

// 初始化
        $wechatPay = new \app\common\service\WechatPayV3($config);

// JSAPI下单示例
        try {
            $params = [
                'appid' => $appid,
                'description' => "支付{$order['class_name']}课程",
                'out_trade_no' => $out_trade_no,
                'amount' => $amount, // 单位：分
                'notify_url' => $notify_url,
                'openid' => $openid
            ];

            $result = $wechatPay->jsapiOrder($params);
            $jsApiParams = $wechatPay->getJsApiParams($result['prepay_id']);

            // 返回给前端调起支付
            $this->success('用deepseek-v3-0324大模型提供的原生方法实现参数获取', $jsApiParams);
        } catch (Exception $e) {
            return json(['code' => 500, 'msg' => $e->getMessage()]);
        }

    }

    public function createPrePayOrder5(Request $request)
    {

        // 获取用户信息（假设已经登录）
        $user = $this->auth->getUser();
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_id = $request->param('order_id');
        $order = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', $order_id)
            ->field("u.username,c.name as class_name,o.*")
            ->find();

        if (!$order) {
            return json(['code' => 404, 'msg' => '订单不存在']);
        }

        // 检查订单状态，避免重复支付
        if ($order['pay_status'] == 1) {
            return json(['code' => 400, 'msg' => '订单已支付']);
        }

        // 获取用户openid（假设已存储在用户表中）
        $openid = $user['oid'];

        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        // 获取小程序用户openid
        $amount = $order['amount'] * 100;
        $orderNo = date('YmdHis') . mt_rand(1000, 9999);
        $appid = config('wechatpay.appid');
        $mchid = config('wechatpay.mchid');
        $serial_no = config('wechat.pay.mchSerialNo');
        $key_path = config('wechatpay.private_key');; // 私钥路径
        $api_key = config('wechatpay.apiv3_key');

        $openid = $openid;
        $out_trade_no = $orderNo;
        $notify_url = config('wechatpay.notify_url');

        // 配置信息
        $mchPrivateKeyFile = $key_path; // 商户私钥文件路径
        $mchSerialNo = $serial_no; // 商户证书序列号
        $apiV3Key = $api_key;     // API v3密钥

// 读取私钥
        $privateKey = openssl_get_privatekey(file_get_contents($mchPrivateKeyFile));
        if (!$privateKey) {
            die('商户私钥读取失败');
        }
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';

        // 请求数据
        $requestData = [
            'appid' => $appid,
            'mchid' => $mchid,
            'description' => "支付{$order['class_name']}课程",
            'out_trade_no' => $out_trade_no,
            'notify_url' => $notify_url,
            'amount' => [
                'total' => $amount,
                'currency' => 'CNY'
            ],
            'payer' => [
                'openid' => $openid
            ]
        ];

        // 将请求数据转为JSON
        $jsonData = json_encode($requestData);

        // 生成随机字符串
        $nonceStr = $this->createNonceStr();

        // 获取时间戳
        $timestamp = time();

        // 构造签名字符串
        $method = 'POST';
        $canonicalUrl = '/v3/pay/transactions/jsapi';
        $signStr = $method . "\n" . $canonicalUrl . "\n" . $timestamp . "\n" . $nonceStr . "\n" . $jsonData . "\n";

        // 签名
        openssl_sign($signStr, $signature, $privateKey, 'sha256WithRSAEncryption');
        $sign = base64_encode($signature);

        // 构造认证头
        $authHeader = 'WECHATPAY2-SHA256-RSA2048 ' .
            'mchid="' . $mchid . '",' .
            'nonce_str="' . $nonceStr . '",' .
            'signature="' . $sign . '",' .
            'timestamp="' . $timestamp . '",' .
            'serial_no="' . $mchSerialNo . '"';

        // 发起请求
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Accept: application/json',
            'Content-Type: application/json',
            'Authorization: ' . $authHeader,
            'User-Agent: FastAdmin/WxPayV3'
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        // 解析返回结果
        $responseData = json_decode($response, true);

        // 预支付交易会话标识
        if ($httpCode == 200 && isset($responseData['prepay_id'])) {
            // 生成小程序/JSAPI调起支付的参数
            $payParams = $this->generatePayParams($appid, $responseData['prepay_id'], $privateKey);
            $responseData['pay_params'] = $payParams;
        }
        $this->success('用https://claude.ai/大模型提供的原生方法实现参数获取', $responseData);
        return [
            'http_code' => $httpCode,
            'response' => $responseData
        ];

    }


    public function createPrePayOrder6(Request $request)
    {

        // 获取用户信息（假设已经登录）
        $user = $this->auth->getUser();
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_id = $request->param('order_id');
        $order = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', $order_id)
            ->field("u.username,c.name as class_name,o.*")
            ->find();

        if (!$order) {
            return json(['code' => 404, 'msg' => '订单不存在']);
        }

        // 检查订单状态，避免重复支付
        if ($order['pay_status'] == 1) {
            return json(['code' => 400, 'msg' => '订单已支付']);
        }

        // 获取用户openid（假设已存储在用户表中）
        $openid = $user['oid'];

        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        function generateOrderNo($userId)
        {
            return date('YmdHis') . str_pad($userId, 6, '0', STR_PAD_LEFT) . mt_rand(1000, 9999);
        }

        // 获取小程序用户openid
        $amount = $order['amount'];
        $orderNo = date('YmdHis') . mt_rand(1000, 9999);
        $appid = config('wechatpay.appid');
        $mchid = config('wechatpay.mchid');
        $serial_no = config('wechat.pay.mchSerialNo');
        $key_path = config('wechatpay.private_key');; // 私钥路径
        $api_key = config('wechatpay.apiv3_key');

        $openid = $openid;
        $out_trade_no = $orderNo;
        $notify_url = config('wechatpay.notify_url');

        // 配置参数
        $config = [
            'mchid' => $mchid,
            'api_v3_key' => $api_key,
            'serial_no' => $serial_no,
            'private_key_path' => $key_path,
            'appid' => $appid,
            'openid' => $openid,
            'notify_url' => $notify_url,
            'out_trade_no' => $out_trade_no,
            'amount' => $amount,
            'description' => "支付{$order['class_name']}课程",
        ];

// 初始化
        $wechatPay = new WechatPayV2($config);

// JSAPI下单示例
        try {
            $orderParams = [
                'body' => $config['description'],
                'out_trade_no' => $config['out_trade_no'],
                'total_fee' => $config['amount'], // 单位：元
                'openid' => $config['openid'],
            ];

            $jsApiParams = $wechatPay->createOrder($orderParams);

            // 返回给前端调起支付
            $this->success('用claude-3-7-sonnet-20250219大模型提供的原生方法wxpayV2实现参数获取', $jsApiParams);
        } catch (Exception $e) {
            return json(['code' => 500, 'msg' => $e->getMessage()]);
        }

    }


    public function createPrePayOrder7(Request $request)
    {

        // 获取用户信息（假设已经登录）
        $user = $this->auth->getUser();
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_id = $request->param('order_id');
        $order = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', $order_id)
            ->field("u.username,c.name as class_name,o.*")
            ->find();

        if (!$order) {
            return json(['code' => 404, 'msg' => '订单不存在']);
        }

        // 检查订单状态，避免重复支付
        if ($order['pay_status'] == 1) {
            return json(['code' => 400, 'msg' => '订单已支付']);
        }

        // 获取用户openid（假设已存储在用户表中）
        $openid = $user['oid'];

        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        // 获取小程序用户openid
        $amount = $order['amount'];
        $orderNo = date('YmdHis') . mt_rand(1000, 9999);
        $appid = config('wechatpay.appid');
        $mchid = config('wechatpay.mchid');
        $serial_no = config('wechat.pay.mchSerialNo');
        $key_path = config('wechatpay.private_key');; // 私钥路径
        $api_key = config('wechatpay.apiv3_key');

        $openid = $openid;
        $out_trade_no = $orderNo;
        $notify_url = config('wechatpay.notify_url');
// 使用示例
        $config = [
            'appid' => $appid,
            'mch_id' => $mchid,
            'key' => $api_key,
            'notify_url' => $notify_url,
        ];

        $openid = $openid;
        $body = '测试商品';
        $out_trade_no = $out_trade_no;
        $total_fee = (int)($amount * 100); // 1分钱测试

// 统一下单
        $result = $this->unifiedOrder($openid, $body, $out_trade_no, $total_fee, $config);

        if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
            // 获取JSAPI支付参数
            $jsApiParams = $this->getJsApiParams($result['prepay_id'], $config);

            // 返回给前端
            echo json_encode([
                'code' => 1,
                'msg' => '用deepseek-v3-0324大模型提供的原生方法wxpayV2实现参数获取',
                'data' => $jsApiParams,
            ]);
        } else {
            echo json_encode([
                'code' => 0,
                'msg' => $result['return_msg'] ?? $result['err_code_des'] ?? '支付失败',
            ]);
        }

    }

    /**
     * 生成签名
     * @param array $params 参数数组
     * @param string $key 商户密钥
     * @return string 签名
     */
    function generateSign($params, $key)
    {
        // 过滤空值
        $params = array_filter($params);
        // 按键名排序
        ksort($params);
        // 拼接字符串
        $string = urldecode(http_build_query($params)) . '&key=' . $key;
        // MD5加密并转为大写
        return strtoupper(md5($string));
    }

    /**
     * JSAPI/小程序下单
     * @param string $openid 用户openid
     * @param string $body 商品描述
     * @param string $out_trade_no 商户订单号
     * @param int $total_fee 金额(分)
     * @param array $config 配置数组
     * @return array 返回结果
     */
    function unifiedOrder($openid, $body, $out_trade_no, $total_fee, $config)
    {
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';

        $params = [
            'appid' => $config['appid'],
            'mch_id' => $config['mch_id'],
            'nonce_str' => md5(uniqid()),
            'body' => $body,
            'out_trade_no' => $out_trade_no,
            'total_fee' => $total_fee,
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
            'notify_url' => $config['notify_url'],
            'trade_type' => 'JSAPI',
            'openid' => $openid,
        ];

        // 生成签名
        $params['sign'] = generateSign($params, $config['key']);

        // 构造XML数据
        $xml = '<xml>';
        foreach ($params as $key => $val) {
            $xml .= "<{$key}>{$val}</{$key}>";
        }
        $xml .= '</xml>';

        // 发送请求
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $response = curl_exec($ch);
        curl_close($ch);

        // 解析XML响应
        $result = json_decode(json_encode(simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

        return $result;
    }

    /**
     * 获取JSAPI支付参数
     * @param string $prepay_id 预支付交易会话标识
     * @param array $config 配置数组
     * @return array 支付参数
     */
    function getJsApiParams($prepay_id, $config)
    {
        $params = [
            'appId' => $config['appid'],
            'timeStamp' => (string)time(),
            'nonceStr' => md5(uniqid()),
            'package' => 'prepay_id=' . $prepay_id,
            'signType' => 'MD5',
        ];

        $params['paySign'] = generateSign($params, $config['key']);

        return $params;
    }

    /**
     * 生成随机字符串
     */
    function createNonceStr($length = 32)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 生成调起支付的参数
     */
    function generatePayParams($appid, $prepayId, $privateKey)
    {
        $nonceStr = $this->createNonceStr();
        $timestamp = (string)time();

        // JSAPI和小程序支付调起支付的参数
        $package = 'prepay_id=' . $prepayId;

        // 构造签名字符串
        $signStr = $appid . "\n" . $timestamp . "\n" . $nonceStr . "\n" . $package . "\n";

        // 签名
        openssl_sign($signStr, $signature, $privateKey, 'sha256WithRSAEncryption');
        $sign = base64_encode($signature);

        return [
            'appId' => $appid,
            'timeStamp' => $timestamp,
            'nonceStr' => $nonceStr,
            'package' => $package,
            'signType' => 'RSA',
            'paySign' => $sign
        ];
    }

    private function rsaSign($message, $key_path)
    {
        $privateKey = openssl_get_privatekey(file_get_contents($key_path));
        openssl_sign($message, $signature, $privateKey, 'sha256WithRSAEncryption');
        return base64_encode($signature);
    }

    private function getAuthorizationToken($method, $url, $body, $mchid, $serial_no, $key_path)
    {
        $timestamp = time();
        $nonce = bin2hex(random_bytes(16));

        $message = "{$method}\n{$url}\n{$timestamp}\n{$nonce}\n{$body}\n";
        $signature = $this->rsaSign($message, $key_path);

        $token = sprintf(
            'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $mchid,
            $nonce,
            $timestamp,
            $serial_no,
            $signature
        );

        return $token;
    }

    private function curlPost($url, $data, $headers)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }

    public function createPrePayOrder1(Request $request)
    {
        // 获取用户信息（假设已经登录）
        $user = $this->auth->getUser();
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_id = $request->param('order_id');
        $order = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', $order_id)
            ->field("u.username,c.name as class_name,o.*")
            ->find();

        if (!$order) {
            return json(['code' => 404, 'msg' => '订单不存在']);
        }

        // 检查订单状态，避免重复支付
        if ($order['pay_status'] == 1) {
            return json(['code' => 400, 'msg' => '订单已支付']);
        }

        // 获取用户openid（假设已存储在用户表中）
        $openid = $user['oid'];

        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        function generateOrderNo($userId)
        {
            return date('YmdHis') . str_pad($userId, 6, '0', STR_PAD_LEFT) . mt_rand(1000, 9999);
        }

        //FastAdmin 微信支付 APIv3 JSAPI/小程序下单 用原始代码
        try {
            $payService = new WeChatPayService();

            $orderData = [
                'description' => $order['title'] ?? '商品支付',
                'out_trade_no' => generateOrderNo($user['uid']),
                'total' => $order['amount'] * 100 // 转为分
            ];

            $params = $payService->jsapiOrder($orderData, $user['oid']);

            return json([
                'code' => 200,
                'data' => $params,
                'msg' => '用WeChatPayService类实现参数获取'
            ]);

        } catch (\Exception $e) {
            Log::error('支付下单失败: ' . $e->getMessage());
            return json(['code' => 500, 'msg' => $e->getMessage()]);
        }


    }

    /**
     * 支付回调通知
     */
    public function notify()
    {
        $this->native_notify();
    }

    /**
     * 处理支付成功逻辑
     */
    private function handlePaymentSuccess(array $data)
    {
        Db::startTrans();
        try {
            // 1. 更新订单状态
            Db::name('order')
                ->where('order_sn', $data['out_trade_no'])
                ->update([
                    'pay_status' => 1,
                    'pay_time' => date('Y-m-d H:i:s'),
                    'transaction_id' => $data['transaction_id']
                ]);

            // 2. 记录支付日志
            Db::name('pay_log')->insert([
                'order_sn' => $data['out_trade_no'],
                'transaction_id' => $data['transaction_id'],
                'amount' => $data['amount']['total'] / 100,
                'pay_data' => json_encode($data),
                'create_time' => time()
            ]);

            // 3. 其他业务逻辑...

            Db::commit();

        } catch (\Exception $e) {
            Db::rollback();
            Log::error('订单处理失败: ' . $e->getMessage());
            throw $e;
        }
    }

// 生成随机字符串
    private function generateNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

// 生成支付签名
    private function generatePaySign($appId, $timeStamp, $nonceStr, $package)
    {
        $message = $appId . "\n" .
            $timeStamp . "\n" .
            $nonceStr . "\n" .
            $package . "\n";

        $privateKey = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/wxcert/apiclient_key.pem');
        $res = openssl_get_privatekey($privateKey);
        openssl_sign($message, $sign, $res, 'sha256WithRSAEncryption');
        $sign = base64_encode($sign);
        openssl_free_key($res);

        return $sign;
    }


    public function notify3(Request $request)
    {
        $xml = file_get_contents('php://input');
        $responseArray = xmlToArray($xml);

        if (isset($responseArray['return_code']) && $responseArray['return_code'] === 'SUCCESS') {
            // 处理支付成功逻辑
            // 例如更新订单状态、发送通知等

            // 返回成功响应
            $returnData = [
                'return_code' => 'SUCCESS',
                'return_msg' => 'OK',
            ];
            echo arrayToXml($returnData);
        } else {
            // 返回失败响应
            $returnData = [
                'return_code' => 'FAIL',
                'return_msg' => '签名失败',
            ];
            echo arrayToXml($returnData);
        }
    }


    /**
     * 获取报名详情
     */
    public
    function getEnrollmentDetail()
    {
        // 获取请求参数中的项目 ID
        $id = input('id/d'); // 获取项目 ID（整数类型）

        if (!$id) {
            return json(['code' => 400, 'msg' => '缺少项目 ID']);
        }

        // 查询项目表
        $info = Db::name('course_table_autoplan_temp')
            ->alias('t')
            ->join('fa_course_table tb', 'tb.id=t.table_id', 'INNER')
            ->join('eb_school s', 'tb.school_id=s.id', 'INNER')
            ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
            ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER')
            ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER')
            ->field("c.id,tb.name as project_name,tb.start_date,tb.end_date,s.name as school_name,c.num,c.intro,c.credit_exchange,c.description,c.num-apply_num as remaining_slots,o2.name as ocation_name,c.name as className,c.cover,subjectlv_id_b,c.price,tb.start_date")
            ->where('c.id', $id)
            ->group("t.ocation_id,t.classes_id")
            ->select();
        foreach ($info as $key => $value) {
            $info[$key]['cover'] = preg_replace('/^https?\:\/\/.*\.myqcloud\.com\//', 'https://tcimg.gxyushengwenhua.com/', $value['cover']);
        }
        if (!$info) {
            return json(['code' => 404, 'msg' => '事项不存在']);
        }

        return json([
            'code' => 200,
            'msg' => 'success',
            'data' => $info
        ]);
    }

    /**
     * 获取招生简章列表
     */
    public
    function getEnrollmentList()
    {
        $id = input('id/d');
        $latitude = $this->request->get('latitude');
        $longitude = $this->request->get('longitude');
        if ($latitude == "") {
            $latitude = 22.81000000000000;
        }
        if ($longitude == "") {
            $longitude = 108.35528564453125;
        }
        // 查询招生简章表
        $enrollmentList = Db::name('course_table_autoplan')
            ->alias('t')
            ->join('fa_course_table tb', 'tb.id=t.table_id', 'INNER')
            ->join('eb_school s', 'tb.school_id=s.id', 'INNER')
            ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
            ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER')
            ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER')
            ->field("c.id,tb.name as project_name,tb.start_date,o.rel_type,o.address_lv_octionlevel,tb.end_date,s.name as school_name,c.num,c.intro,c.credit_exchange,c.description,c.num-apply_num as remaining_slots,o2.name as ocation_name,c.name as className,c.cover,subjectlv_id_b,c.price,tb.start_date,o.`longitude`,o.`latitude`, (
        6371 * ACOS(
            COS(RADIANS($latitude)) * COS(RADIANS(o.latitude)) * COS(RADIANS(o.longitude) - RADIANS($longitude)) +
            SIN(RADIANS($latitude)) * SIN(RADIANS(o.latitude))
        )
    ) AS km")
            ->group("t.ocation_id,t.classes_id")
            ->select();

        foreach ($enrollmentList as $key => $value) {
            $tags = [];
            $enrollmentList[$key]['cover'] = preg_replace('/^https?\:\/\/.*\.myqcloud\.com\//', 'https://tcimg.gxyushengwenhua.com/', $value['cover']);
            if ($enrollmentList[$key]['address_lv_octionlevel'] == "1") {
                $enrollmentList[$key]['address_lv_octionlevel'] = "一级教学点";
            } else if ($enrollmentList[$key]['address_lv_octionlevel'] == "2") {
                $enrollmentList[$key]['address_lv_octionlevel'] = "一级教学点";
            } else if ($enrollmentList[$key]['address_lv_octionlevel'] == "3") {
                $enrollmentList[$key]['address_lv_octionlevel'] = "二级教学点";
            } else {
                $enrollmentList[$key]['address_lv_octionlevel'] = "二级教学点";
            }
            if ($enrollmentList[$key]['rel_type'] == "2") {
                $tags[] = "社区";
            } else if ($enrollmentList[$key]['rel_type'] == "1") {
                //$tags[]="学校";
            }
            $tags[] = $enrollmentList[$key]['subjectlv_id_b'];
            $tags[] = "积分兑换";
            $enrollmentList[$key]['tags'] = array_unique($tags);
        }

        if (!$enrollmentList) {
            return json(['code' => 404, 'msg' => '该学校的招生事项不存在']);
        }

        return json([
            'code' => 200,
            'msg' => 'success',
            'data' => $enrollmentList
        ]);
    }


    //招名动作
    public
    function applyEnrollment()
    {
        $json = file_get_contents('php://input');
        $data = json_decode($json, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            return json(['code' => 400, 'msg' => 'Invalid JSON data']);
        }
        $classes = $data['courses'];
        $user = $this->auth->getUser();
        $result = model("app\admin\model\Apply")->baoming($classes, $user);
        if (is_array($result) && $result['code'] != 200) {
            $this->error($result['msg'], $result['data'], 400);
        }
        return $result;
    }


    /**
     * 学校端-报名
     */
    public
    function getInfo()
    {
        // 获取请求参数
        $id = input('id'); // 假设通过ID获取项目详情


        // 课程报名
        if ($_REQUEST["coursetType"] == 1) {


            // 查询课程项目排课计划表及相关联的数据
            $info = Db::name('course_table_autoplan_temp') // 课程项目排课计划表
            ->alias('t')
                ->join('fa_course_table tb', 'tb.id=t.table_id', 'INNER') // 课程项目基础表
                ->join('eb_school s', 'tb.school_id=s.id', 'INNER') // 关联学校
                ->join('eb_classes c', 'c.id=t.classes_id', 'INNER') // 关联班级
                ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER') // 关联上课地点
                ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER') // 关联上课地点父级
                ->field("c.id, tb.name as project_name, tb.start_date, tb.end_date, 
                     s.name as school_name, c.num, c.intro, c.credit_exchange, 
                     c.description, c.num - apply_num as remaining_slots, 
                     o2.name as ocation_name, c.name as className, c.cover, 
                     subjectlv_id_b, c.price, tb.start_date")
                ->where('c.id', $id)
                ->group("t.ocation_id, t.classes_id")
                ->select();

            if (empty($info)) {
                return json(['code' => 404, 'msg' => '项目不存在']);
            }


        } elseif ($_REQUEST["coursetType"] == 2) { //活动报名

        } elseif ($_REQUEST["coursetType"] == 3) { //预约报名
            // 查询课程项目排课计划表及相关联的数据
            $info = Db::name('course_table_autoplan_temp') // 课程项目排课计划表
            ->alias('t')
                ->join('fa_course_table tb', 'tb.id=t.table_id', 'INNER') // 课程项目基础表
                ->join('eb_school s', 'tb.school_id=s.id', 'INNER') // 关联学校
                ->join('eb_classes c', 'c.id=t.classes_id', 'INNER') // 关联班级
                ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER') // 关联上课地点
                ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER') // 关联上课地点父级
                ->field("c.id, tb.name as project_name, tb.start_date, tb.end_date, 
                     s.name as school_name, c.num, c.intro, c.credit_exchange, 
                     c.description, c.num - apply_num as remaining_slots, 
                     o2.name as ocation_name, c.name as className, c.cover, 
                     subjectlv_id_b, c.price, tb.start_date")
                ->where('c.id', $id)
                ->group("t.ocation_id, t.classes_id")
                ->select();

            if (empty($info)) {
                return json(['code' => 404, 'msg' => '项目不存在']);
            }
        }

        // 返回项目详情
        return json([
            'code' => 200,
            'data' => $info, // 假设只返回第一条数据
        ]);
    }


    public
    function getSubjectNames()
    {
        // 获取最新的前3个项目
        $latestProjects = Db::name('course_table')
            ->order('create_time', 'desc')
            ->limit(3)
            ->select();

        $projectIds = [];
        foreach ($latestProjects as $project) {
            $projectIds[] = $project['id'];
        }

        // 获取这些项目对应的所有subject_lv和ocation_id
        $result = Db::name('course_table_autoplan_temp')
            ->whereIn('table_id', $projectIds)
            ->field('subject_lv, ocation_id')
            ->select();

        $subjectLvs = [];
        $ocationIds = [];
        foreach ($result as $item) {
            $subjectLvs[] = $item['subject_lv'];
            $ocationIds[] = $item['ocation_id'];
        }

        // 去除重复的 subject_lv 和 ocation_id
        $subjectLvs = array_unique($subjectLvs);
        $ocationIds = array_unique($ocationIds);

        // 将 subject_lv 转换为包含 ID 和名称的数组
        $subjects = Db::name('subject_lv')
            ->whereIn('id', $subjectLvs)
            ->field('id, name')
            ->select();
        $subjectNames = [];
        foreach ($subjects as $subject) {
            $subjectNames[] = [
                'id' => $subject['id'],
                'name' => $subject['name']
            ];
        }

        // 查询教学点信息，假设教学点信息存储在 fa_location 表中
        $locations = Db::name('fa_ocation')
            ->whereIn('id', $ocationIds)
            ->field('id, name')
            ->select();
        $locationNames = [];
        foreach ($locations as $location) {
            $locationNames[] = [
                'id' => $location['id'],
                'name' => $location['name']
            ];
        }

        $data = [
            'subjectNames' => $subjectNames,
            'locationNames' => $locationNames
        ];

        return json($data);
    }

// 获取就近教学点
    public
    function getNearbyLocations()
    {
        // 获取最新的前3个项目
        $latestProjects = Db::name('course_table')
            ->order('create_time', 'desc')
            ->limit(3)
            ->select();

        $projectIds = [];
        foreach ($latestProjects as $project) {
            $projectIds[] = $project['id'];
        }

        // 获取这些项目对应的所有 subject_lv 和 ocation_id
        $result = Db::name('course_table_autoplan_temp')
            ->whereIn('table_id', $projectIds)
            ->field('ocation_id')
            ->select();


        $ocationIds = [];
        foreach ($result as $item) {
            $ocationIds[] = $item['ocation_id'];
        }

        // 去除重复的 ocation_id
        $ocationIds = array_unique($ocationIds);


        // 获取请求中的经纬度
        $latitude = $this->request->get('latitude', 22.81); // 默认纬度
        $longitude = $this->request->get('longitude', 108.35528564453125); // 默认经度

        // 查询教学点信息，假设教学点信息存储在 fa_ocation 表中，并计算距离
        $locations = Db::name('fa_ocation')
            ->whereIn('id', $ocationIds)
            ->field("id, name, `longitude`, `latitude`, (
            6371 * ACOS(
                COS(RADIANS($latitude)) * COS(RADIANS(`latitude`)) * COS(RADIANS(`longitude`) - RADIANS($longitude)) +
                SIN(RADIANS($latitude)) * SIN(RADIANS(`latitude`))
            )
        ) AS km")
            ->select();

        $locationNames = [];
        foreach ($locations as $location) {
            $locationNames[] = [
                'id' => $location['id'],
                'name' => $location['name'],
                'distance' => $location['km']
            ];
        }

        $data = [
            'locationNames' => $locationNames
        ];

        return json($data);
    }

// 根据报表点ID 获取项目信息

    /**
     * 获取课程详细信息
     * @return \think\response\Json
     */
    public
    function getCourseDetails()
    {
        $ocationId = input('ocation_id/d');

        if (!$ocationId) {
            return json(['code' => 400, 'msg' => '缺少教学点 ID']);
        }

        $data_temp = Db::name('Ocation')->alias('o')
            ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER')
            ->join('eb_school s', 's.id=o.rel_id', 'LEFT')
            ->field('o.id,o.name,o.address,o.`longitude`,o.`latitude`,o.image,o.rel_type,o.rel_id,o.address,s.name school_name')
            ->where('o.id', $ocationId)->select();
        $data = $data_temp[0];
        // 查找教学点的图片，没有的话，就以其中一个班的封面做为封面
        if ($data['image'] == null) {
            $data['image'] = Db::name('course_table_autoplan_temp')
                ->alias('t')
                ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
                ->where('t.ocation_id', $ocationId)
                ->value('c.cover');
        }
        if ($data['image'] == null) {
            $data['image'] = Db::name('course_table_autoplan_temp')
                ->alias('t')
                ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
                ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER')
                ->join('fa_ocation o2', 'o2.id=t.ocation_id', 'INNER')
                ->where('o2.id', $ocationId)
                ->value('c.cover');
        }
        if ($data['image'] == null) {
            $data['image'][] = "/uploads/20250304/d9ad41727f2d423a38d4860c16df76ea.jpg";
        }
        if (!isset($data['isCollected'])) {
            $data['isCollected'] = $this->isCollected($ocationId, 1);
        }

        $courseDetails = Db::name('course_table_autoplan_temp')
            ->alias('t')
            ->join('fa_course_table tb', 'tb.id=t.table_id', 'INNER')
            ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
            ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER')
            ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER')
            ->join('fa_subject_lv sl', 'sl.id=c.subject_lv', 'INNER')
            ->join('eb_apply_form_item a', "a.classes_id=c.id", 'LEFT')
            ->field("
            o.id as ocation_id,
            o.name as ocation_name,
            tb.name as course_name,
            o2.name as parent_location_name,
            o2.id as parent_location_id,
            c.intro as description,
            o.address as address,
            c.description as detail_description,
            o.`longitude`,
            o.`latitude`,
            c.price,
            c.intro,
            c.rel_type,
            c.id as classes_id,
            c.name as classes_name,
            sl.name as subject_name,
            sl.id as subjectlv_id,
            a.id as apply_id,
            c.num - apply_num as remaining_slots
        ")
            ->where('o2.id', $ocationId)
            ->whereOr('o.id', $ocationId)
            ->group("t.ocation_id,t.classes_id")
            ->select();

        $data["courseList"] = $courseDetails;
        return json([
            'code' => 200,
            'msg' => 'success',
            'data' => $data
        ]);
    }


    /**
     * 根据经纬度获取附近课程详细信息
     * @return \think\response\Json
     */
    public
    function getNearbyCourseDetails()
    {
        $latitude = $this->request->get('latitude', 22.81);
        $longitude = $this->request->get('longitude', 108.35528564453125);
        $user = $this->auth->getUser(true);

        $query = Db::name('course_table_autoplan_temp')
            ->alias('t')
            ->join('fa_course_table tb', 'tb.id=t.table_id', 'INNER')
            ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
            ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER')
            ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER')
            ->join('fa_subject_lv sl', 'sl.id=c.subject_lv', 'INNER')
            ->join('fa_address_lv o3', 'sl.addresslv_id=o3.id', 'INNER');

        if (!empty($user)) {
            $query->join('eb_apply_form_item a', "a.classes_id=c.id AND a.user_id='{$user['uid']}'", 'LEFT');
        } else {
            $query->join('eb_apply_form_item a', "a.classes_id=c.id", 'LEFT');
        }

        $courseDetails = $query
            ->field("
        tb.name as course_name,
        o2.name as location_name,
        c.name as className,
        c.intro as description,
        o.id as ocation_id,
        o.address as address,
        c.description as detail_description,
        o.`longitude`,
        o.`latitude`,
        c.price,
        c.rel_type,
        c.id as classes_id,
        a.id as apply_id,
        o3.name as address_lv,
        sl.name as subject_name,
        sl.id as subjectlv_id,
        c.num - apply_num as remaining_slots,
        (
            6371 * ACOS(
                COS(RADIANS($latitude)) * COS(RADIANS(o.latitude)) * COS(RADIANS(o.longitude) - RADIANS($longitude)) +
                SIN(RADIANS($latitude)) * SIN(RADIANS(o.latitude))
            )
        ) AS distance
    ")
            ->group("t.ocation_id,t.classes_id")
            ->order('distance', 'asc')
            ->select();
        $ocationInfo = [];
        $ocationInfo["ocation_name"] = $courseDetails[0]['location_name'];
        $ocationInfo["ocation_id"] = $courseDetails[0]['ocation_id'];
        $ocationInfo["address"] = $courseDetails[0]['address'];
        $subjects = Db::name('fa_course_table_autoplan_temp')->alias("pl")->join("fa_subject_lv sl", "pl.subject_lv=sl.id")->join("address_lv ad", "ad.id=sl.addresslv_id")->field("sl.id as value,CONCAT(ad.name,' ',sl.name) as text")->group("sl.id")->select();
        //$subjects=array_unique($subjects,"text");
        if (!$courseDetails) {
            return json(['code' => 404, 'msg' => '未找到附近课程信息']);
        }

        return json([
            'code' => 200,
            'msg' => 'success',
            'data' => array('ocationInfo' => $ocationInfo, 'subjects' => $subjects, 'courseDetails' => $courseDetails)
        ]);
    }


    /**
     * 根据学科名称或学科级别 ID 获取课程详细信息
     * @return \think\response\Json
     */
    public
    function getCourseDetailsBySubject2()
    {
        $subjectName = $this->request->get('subject_name');
        $subjectLvId = $this->request->get('subjectlv_id/d');

        if (!$subjectName && !$subjectLvId) {
            return json(['code' => 400, 'msg' => '缺少学科名称或学科级别 ID']);
        }
        $user = $this->auth->getUser(true);
        $user['uid'] = $user['uid'] ?? 0;
        $query = Db::name('course_table_autoplan_temp')
            ->alias('t')
            ->join('fa_course_table tb', 'tb.id=t.table_id', 'INNER')
            ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
            ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER')
            ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER')
            ->join('fa_subject_lv sl', 'sl.id=c.subject_lv', 'INNER')
            ->join('eb_apply_form_item a', "a.classes_id=c.id and a.user_id='{$user['uid']}'", 'LEFT')
            ->field("
            tb.name as course_name,
            o2.name as location_name,
            c.name as className,
            c.intro as description,
            o.address as address,
            c.description as detail_description,
            o.`longitude`,
            o.`latitude`,
            c.price,
            c.rel_type,
            c.id as classes_id,
            c.name as classes_name,
            a.id as apply_id,
            sl.name as subject_name,
            sl.id as subjectlv_id,
            c.num - apply_num as remaining_slots
        ")
            ->group("t.ocation_id,t.classes_id");

        if ($subjectName) {
            $query->where('sl.name', $subjectName);
        } elseif ($subjectLvId) {
            $query->where('sl.id', $subjectLvId);
        }

        $courseDetails = $query->select();


        if (!$courseDetails) {
            return json(['code' => 404, 'msg' => '未找到相关课程信息']);
        }

        return json([
            'code' => 200,
            'msg' => 'success',
            'data' => $courseDetails
        ]);
    }

    /**
     * 根据学科名称或学科级别 ID 获取课程详细信息
     * @return \think\response\Json
     */
    public
    function getCourseDetailsBySubject()
    {
        $subjectName = $this->request->get('subject_name');
        $subjectLvId = $this->request->get('subjectlv_id/d');

        if (!$subjectName && !$subjectLvId) {
            return json(['code' => 400, 'msg' => '缺少学科名称或学科级别 ID']);
        }

        // 使用Event控制器的基础查询方法
        $eventController = new \app\api\controller\v1\Event();
        $eventController->request = $this->request;
        $eventController->auth = $this->auth;
        $eventController->notApplyed = true; //不要列出已报名的课程

        $ocationInfo = model("\app\admin\model\Ocation")->getOcationInfo($this->request->get('ocation_id/d'), $this->request->get('cityName'));
        $params = [
            'page' => $this->request->get('page', 1),
            'limit' => $this->request->get('limit', 50), // 课程详情可能需要更多数据
            'subject_name' => $subjectName,
            'subjectlv_id' => $subjectLvId,
            'is_started' => 1,
            'latitude' => $ocationInfo['latitude'],
            'longitude' => $ocationInfo['longitude'],
            'order' => 'km asc',
        ];

        try {
            // 调用基础查询方法
            $courseDetails = $eventController->baseCourseQuery($params);

            if (!$courseDetails) {
                return json(['code' => 404, 'msg' => '未找到相关课程信息']);
            }

            // 格式化返回数据，保持与原方法相同的字段结构
            $formattedData = [];
            // 排除重复的classes_id
            $uniqueClasses = [];
            $result = [];

            foreach ($courseDetails as $item) {
                $classId = $item['classes_id'] ?? null;
                if ($classId !== null && !isset($uniqueClasses[$classId])) {
                    $uniqueClasses[$classId] = true;  // Mark this class ID as seen
                    $result[] = $item;  // Add the first occurrence to results
                }
            }

            // Replace the original array with filtered results
            $courseDetails = $result;
            foreach ($courseDetails as $item) {
                // 按照enrollmentDetails页面的方式处理description字段
                $item['description'] = preg_replace('/<title>.*<\/title>|<meta[^>]+>/Uisx', '', $item['description']);
                $item['description'] = preg_replace('/<h[1-6]>.*<\/h[1-6]>|<meta[^>]+>/Uisx', '', $item['description']);
                $item['description'] = preg_replace('/<p>[^>]+>/Uisx', "<p>", $item['description']);
                $item['description'] = preg_replace('/\s+/isx', "", $item['description']);
                $item['description'] = strip_tags($item['description'], "<p><br>");
                preg_match('/^(.*?<\/p>)\s*<p/s', $item['description'], $matches);
                if (isset($matches[1])) {
                    $item['description'] = preg_replace("/<\/?p>/", '', $matches[1]);
                }

                $formattedData[] = [
                    'address' => $item['address'] ?? '',
                    'classes_id' => $item['classes_id'] ?? '',
                    'classes_type' => $item['classes_type'] ?? '',
                    'subject_name' => $item['subject_name'] ?? '',
                    'classes_name' => $item['classes_name'] ?? '',
                    'course_name' => $item['course_name'] ?? '',
                    'location_name' => $item['location_name'] ?? '',
                    'className' => $item['className'] ?? '',
                    'description' => $item['description'] ?? '',
                    'detail_description' => $item['detail_description'] ?? '',
                    'longitude' => $item['longitude'] ?? '',
                    'latitude' => $item['latitude'] ?? '',
                    'price' => $item['price'] ?? 0,
                    'rel_type' => $item['rel_type'] ?? '',
                    'apply_id' => $item['apply_id'] ?? null,
                    'subjectlv_id' => $item['subjectlv_id'] ?? '',
                    'remaining_slots' => $item['remaining_slots'] ?? 0,
                    'start_date' => $item['start_date'] ?? '',
                    'km' => $item['km'] ?? 0,
                    'tian' => $item['tian'] ?? 0,
                    'cover' => $item['cover'] ? getImg($item['cover'], 200, 200) : '',
                    // 确保包含ocourse_count、weektime、start_time和end_time字段
                    'ocourse_count' => $item['ocourse_count'] ?? 0,
                    'weektime' => $item['weektime'] ?? '',
                    'start_time' => $item['start_time'] ?? '',
                    'end_time' => $item['end_time'] ?? '',
                ];
            }

            $result = Db::table('eb_apply_form')->alias('f')
                ->join("fa_course_table table", "f.table_id=table.id", "left")
                ->where('table_id', $courseDetails[0]['table_id'])
                ->where('f.status', 1)
                ->where('is_close', 0)
                ->where('is_finished', 0)
                ->where('is_user_end', 0)
                ->where('is_end', 0)
                //->where('dead_time', '>', date('Y-m-d H:i:s'))
                ->field('f.id,table.name table_name,table_id,apply_type,title,intro,cover,`male_age_min`,`male_age_max`,`female_age_min`,`female_age_max`,f.start_time,f.end_time,max_num,is_use_integral,dead_time,f.terms_content,f.applyed_count')
                ->select();
            foreach ($formattedData as $key => $item) {
                $formattedData[$key] = array_merge($item, ['terms_content' => $this->format_content($result[0]['terms_content']), 'male_age_min' => $result[0]['male_age_min'], 'male_age_max' => $result[0]['male_age_max'], 'female_age_min' => $result[0]['female_age_min'], 'female_age_max' => $result[0]['female_age_max'], 'applyed_count' => $result[0]['applyed_count']]);
            }


            return json([
                'code' => 200,
                'msg' => 'success',
                'data' => $formattedData
            ]);

        } catch (\Exception $e) {
            return json(['code' => 500, 'msg' => '获取课程信息失败: ' . $e->getMessage()]);
        }
    }

//课程分类
    public
    function getCourseCategories()
    {
        $categories = Db::name('fa_subject_lv')->field("id as value,name as text")->select();
        return json(['code' => 200, 'msg' => 'success', 'data' => $categories]);
    }


    public
    function wxpay3()
    {

        // 引入微信支付 SDK（假设你已经下载了官方 SDK）
        //require_once 'WxPay.Api.php'; // 微信支付 SDK

        function createWechatQRCode($order_id, $total_fee)
        {
            // 配置参数
            $input = new \WxPayUnifiedOrder();
            $input->SetBody("商品描述"); // 商品描述
            $input->SetOut_trade_no($order_id); // 订单号
            $input->SetTotal_fee($total_fee * 100); // 总金额（单位：分）
            $input->SetNotify_url("https://dev002.gxyushengwenhua.com/ysapi/v1/Enrollment/notify.php"); // 支付结果通知地址
            $input->SetTrade_type("NATIVE"); // 扫码支付类型
            $input->SetProduct_id($order_id); // 商品 ID

            // 调用微信支付接口，生成预支付交易会话
            $result = \WxPayApi::unifiedOrder($input);

            // 检查返回结果是否成功
            if (isset($result["code_url"])) {
                return $result["code_url"]; // 返回支付二维码链接
            } else {
                throw new Exception("生成二维码失败：" . json_encode($result));
            }
        }

        // 示例调用
        try {
            $order_id = "1"; // 假设订单号是 123456
            $total_fee = 300; // 假设支付金额是 1 元
            $qr_code_url = createWechatQRCode($order_id, $total_fee);
            echo "支付二维码链接：" . $qr_code_url;
        } catch (Exception $e) {
            echo "错误：" . $e->getMessage();
        }
    }

    public function appNotify()
    {

        // 商户号
        $merchantId = '1682857812';
        // 商户API证书序列号
        $merchantSerialNumber = '5C5D72F82C6A51FCC19C64E0C53661EAF59D7881';
        // 商户私钥文件路径
        $merchantPrivateKeyFilePath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/apiclient_key.pem';
        // 微信支付平台证书文件路径
        $wechatpayCertificateFilePath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/wechatpay_1CCBC1AD439A04D33A713F302466A507B41C347A.pem';
        // 检查文件是否存在且可读
        if (!file_exists($wechatpayCertificateFilePath) || !is_readable($wechatpayCertificateFilePath)) {
            die("微信支付平台证书文件不存在或不可读：$wechatpayCertificateFilePath");
        }
        // 尝试读取证书内容
        $certContent = file_get_contents($wechatpayCertificateFilePath);
        if ($certContent === false) {
            die("无法读取微信支付平台证书文件内容");
        }
        // 检查证书内容
        if (strpos($certContent, '-----BEGIN CERTIFICATE-----') === false) {
            die("微信支付平台证书文件内容格式不正确");
        }
        $rootCaPath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/rootca.pem';
        // 商户API v3密钥
        $merchantApiV3Key = 'Zx8kP9qR2sT5vY7wB3mN6cF1hJ4lK0pX';
        // 读取商户私钥
        $merchantPrivateKey = PemUtil::loadPrivateKey($merchantPrivateKeyFilePath);
        // 读取私钥内容
        $merchantPrivateKey = file_get_contents($merchantPrivateKeyFilePath);
        // 加载微信支付平台证书
        $wechatpayCertificate = PemUtil::loadCertificate($wechatpayCertificateFilePath);
        // 验证私钥是否可以被正确加载
//        $merchantPrivateKeyInstance = openssl_pkey_get_private($merchantPrivateKey);
//        if ($merchantPrivateKeyInstance === false) {
//            throw new \Exception("无法加载私钥，请检查私钥文件格式是否正确");
//        }
        // 如果没有平台证书，可以通过以下代码获取平台证书
        //$this->getPlatformCertificates($rootCaPath,$merchantApiV3Key,$merchantId,$merchantPrivateKey, $merchantSerialNumber, $merchantPrivateKeyFilePath);

        // 读取微信支付平台证书内容
        $wechatpayCertificate = file_get_contents($wechatpayCertificateFilePath);
        if ($wechatpayCertificate === false) {
            throw new \Exception("无法读取微信支付平台证书内容");
        }

        // 解析微信支付平台证书序列号
        $wechatpaySerialNumber = PemUtil::parseCertificateSerialNo($wechatpayCertificate);

        // 构建微信支付 API 客户端
        $builder = Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantSerialNumber,
            'privateKey' => $merchantPrivateKey,
            'certs' => [
                $wechatpaySerialNumber => $wechatpayCertificate
            ],
            'apiV3Key' => $merchantApiV3Key
        ]);

        // 获取微信支付通知数据
        $input = file_get_contents('php://input');
// 获取所有请求头
        $headers = getallheaders();

// 将所有头信息转换为小写键，以便不区分大小写
        $headers = array_change_key_case($headers, CASE_LOWER);

        //将input和headers写入日志文件
        $logFilePath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/log.txt';
        $logMessage = "Input:\n$input\n\nHeaders:\n";
        foreach ($headers as $key => $value) {
            $logMessage .= "$key: $value\n";
        }
        file_put_contents($logFilePath, $logMessage . "\n", FILE_APPEND);


// 检查必要的头信息是否存在
        $requiredHeaders = ['wechatpay-timestamp', 'wechatpay-nonce', 'wechatpay-signature'];
        $missingHeaders = array_diff($requiredHeaders, array_keys($headers));

        if (!empty($missingHeaders)) {
            echo json_encode([
                'code' => 'FAIL',
                'message' => '缺少必要的请求头: ' . implode(', ', $missingHeaders)
            ]);
            exit;
        }

        try {
            $inBody = json_decode($input, true);

            // 使用 WeChatPay\Crypto\Rsa 进行验证
            $publicKey = PemUtil::loadCertificate($wechatpayCertificateFilePath);

            // 构造待签名的字符串
            $message = $headers['wechatpay-timestamp'] . "\n" .
                $headers['wechatpay-nonce'] . "\n" .
                $input . "\n";

            $signature = base64_decode($headers['wechatpay-signature']);

            $verified = Rsa::verify($message, $signature, $publicKey);

            if ($verified) {
                // 验证成功，继续处理
                $resource = $inBody['resource'];
                $associatedData = $resource['associated_data'];
                $nonce = $resource['nonce'];
                $ciphertext = $resource['ciphertext'];

                // 解密通知数据
                $decryptedData = Rsa::decryptOAEP($ciphertext, $merchantApiV3Key, $associatedData, $nonce);
                $decryptedData = json_decode($decryptedData, true);

                // ... 后续处理逻辑 ...
            } else {
                // 验证失败
                throw new \Exception('签名验证失败');
            }
        } catch (\Exception $e) {
            // 处理异常
            echo json_encode([
                'code' => 'FAIL',
                'message' => '处理通知时出现异常：' . $e->getMessage()
            ]);
        }


        try {
            $inBody = json_decode($input, true);
            // 使用 WeChatPay\Crypto\Rsa 进行验证
            $publicKey = PemUtil::loadCertificate($wechatpayCertificateFilePath);

            // 构造待签名的字符串
            $message = $headers['Wechatpay-Timestamp'] . "\n" .
                $headers['Wechatpay-Nonce'] . "\n" .
                $input . "\n";

            $signature = base64_decode($headers['Wechatpay-Signature']);

            $verified = Rsa::verify($message, $signature, $publicKey);

            if ($verified) {
                // 验证成功，继续处理
                $resource = $inBody['resource'];
                $associatedData = $resource['associated_data'];
                $nonce = $resource['nonce'];
                $ciphertext = $resource['ciphertext'];

                // 解密通知数据
                $decryptedData = Rsa::decryptOAEP($ciphertext, $merchantApiV3Key, $associatedData, $nonce);
                $decryptedData = json_decode($decryptedData, true);

                // 处理支付结果
                $outTradeNo = $decryptedData['out_trade_no'];
                $transactionId = $decryptedData['transaction_id'];
                $tradeState = $decryptedData['trade_state'];

                if ($tradeState === 'SUCCESS') {
                    // 支付成功，更新订单状态等操作
                    // 例如：更新数据库中订单状态为已支付
                    // 这里可以添加具体的业务逻辑
                    echo json_encode([
                        'code' => 'SUCCESS',
                        'message' => '成功'
                    ]);
                } else {
                    // 支付失败或其他状态，进行相应处理
                    echo json_encode([
                        'code' => 'FAIL',
                        'message' => '支付失败或状态异常'
                    ]);
                }
            } else {
                // 签名验证失败
                echo json_encode([
                    'code' => 'FAIL',
                    'message' => '签名验证失败'
                ]);
            }
        } catch (\Exception $e) {
            // 处理异常
            echo json_encode([
                'code' => 'FAIL',
                'message' => '处理通知时出现异常：' . $e->getMessage()
            ]);
        }


    }


// 微信二维码缴费
    public
    function wxQRcodepay()
    {
        $orderId = input('order_id/d');
        if (!$orderId) {
            return json(['code' => 400, 'msg' => '缺少订单 ID']);
        }
        $order = Db::name('order')->where('id', $orderId)->find();
        if (!$order) {
            return json(['code' => 404, 'msg' => '订单不存在']);
        }

        // 确保 $order['order_sn'] 有值
        if (empty($order['order_sn'])) {
            return json(['code' => 400, 'msg' => '订单号为空']);
        }

        // 商户号
        $merchantId = '1682857812';
        // 商户API证书序列号
        $merchantSerialNumber = '224C018B99D3099A85BB363F71BBC2F4D5AC556D';
        // 商户私钥文件路径
        $merchantPrivateKeyFilePath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/apiclient_key.pem';
        // 微信支付平台证书文件路径
        $wechatpayCertificateFilePath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/wechatpay_1CCBC1AD439A04D33A713F302466A507B41C347A.pem';
        // 检查文件是否存在且可读
        if (!file_exists($wechatpayCertificateFilePath) || !is_readable($wechatpayCertificateFilePath)) {
            die("微信支付平台证书文件不存在或不可读：$wechatpayCertificateFilePath");
        }
        // 尝试读取证书内容
        $certContent = file_get_contents($wechatpayCertificateFilePath);
        if ($certContent === false) {
            die("无法读取微信支付平台证书文件内容");
        }
        // 检查证书内容
        if (strpos($certContent, '-----BEGIN CERTIFICATE-----') === false) {
            die("微信支付平台证书文件内容格式不正确");
        }
        $rootCaPath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/rootca.pem';
        // 商户API v3密钥
        $merchantApiV3Key = 'Zx8kP9qR2sT5vY7wB3mN6cF1hJ4lK0pX';
        // 读取商户私钥
        $merchantPrivateKey = PemUtil::loadPrivateKey($merchantPrivateKeyFilePath);
        // 读取私钥内容
        $merchantPrivateKey = file_get_contents($merchantPrivateKeyFilePath);
        // 加载微信支付平台证书
        $wechatpayCertificate = PemUtil::loadCertificate($wechatpayCertificateFilePath);
        // 验证私钥是否可以被正确加载
//        $merchantPrivateKeyInstance = openssl_pkey_get_private($merchantPrivateKey);
//        if ($merchantPrivateKeyInstance === false) {
//            throw new \Exception("无法加载私钥，请检查私钥文件格式是否正确");
//        }
        // 如果没有平台证书，可以通过以下代码获取平台证书
        //$this->getPlatformCertificates($rootCaPath,$merchantApiV3Key,$merchantId,$merchantPrivateKey, $merchantSerialNumber, $merchantPrivateKeyFilePath);

        // 读取微信支付平台证书内容
        $wechatpayCertificate = file_get_contents($wechatpayCertificateFilePath);
        if ($wechatpayCertificate === false) {
            throw new \Exception("无法读取微信支付平台证书内容");
        }

        // 解析微信支付平台证书序列号
        $wechatpaySerialNumber = PemUtil::parseCertificateSerialNo($wechatpayCertificate);

        [$statusCode, $response] = $this->wechatPayRequest([
            'mchid' => $merchantId,
            'out_trade_no' => $order['order_sn'],
            'appid' => 'wx2bab6cd1400f48bb',
            'description' => '商品描述',
            'notify_url' => 'https://dev002.gxyushengwenhua.com/ysapi/v1/Enrollment/notify.php',
            'amount' => [
                'total' => intval($order['amount'] * 100),
                'currency' => 'CNY'
            ]
        ], $merchantId, $merchantSerialNumber, $merchantPrivateKeyFilePath, $wechatpayCertificateFilePath);

        if ($statusCode == 200) {
            $result = json_decode($response, true);
            $qrCodeUrl = $result['code_url'];

            // 创建 QR 码
            $qrCode = QrCode::create($qrCodeUrl);

            // 使用 PngWriter 来生成 PNG 图像
            $writer = new PngWriter();
            $result = $writer->write($qrCode);

            // 获取 PNG 图像的二进制数据
            $pngData = $result->getString();

            // 将 PNG 数据转换为 base64 编码
            $qrCodeBase64 = base64_encode($pngData);

            return json([
                'code' => 200,
                'msg' => 'success',
                'data' => [
                    'qr_code' => $qrCodeBase64,
                    'order_id' => $order['id']
                ]
            ]);
        } else {
            return json(['code' => 500, 'msg' => '微信支付下单失败', 'data' => $order]);
        }

    }


    /**
     * 发起微信支付请求
     *
     * @param array $params 支付参数
     * @param string $merchantId 商户号
     * @param string $merchantSerialNumber 商户证书序列号
     * @param string $merchantPrivateKeyFilePath 商户私钥文件路径
     * @param string $wechatpayCertificateFilePath 微信支付平台证书文件路径
     * @return array
     * @throws \Exception
     */
    function wechatPayRequest(array $params, string $merchantId, string $merchantSerialNumber, string $merchantPrivateKeyFilePath, string $wechatpayCertificateFilePath): array
    {
        // 加载商户私钥
        $merchantPrivateKey = file_get_contents($merchantPrivateKeyFilePath);
        if ($merchantPrivateKey === false) {
            throw new \Exception("无法读取商户私钥文件");
        }

        // 准备请求URL
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/native';

        // 生成随机字符串
        $nonce = bin2hex(random_bytes(16));

        // 生成时间戳
        $timestamp = time();

        // 构造签名串
        $message = "POST\n/v3/pay/transactions/native\n{$timestamp}\n{$nonce}\n" . json_encode($params) . "\n";

        // 使用私钥对签名串进行签名
        openssl_sign($message, $signature, $merchantPrivateKey, OPENSSL_ALGO_SHA256);
        $signatureBase64 = base64_encode($signature);

        // 构造 Authorization 头
        $authorization = sprintf(
            'WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $merchantId,
            $nonce,
            $timestamp,
            $merchantSerialNumber,
            $signatureBase64
        );

        // 设置请求头
        $headers = [
            'Accept: application/json',
            'Content-Type: application/json',
            'Authorization: ' . $authorization,
            'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', // 必须包含 User-Agent 字段
        ];

        // 初始化 cURL
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
//        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 忽略 SSL 证书验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 忽略主机名验证

        // 设置 CA 证书路径（可选，如果需要验证微信支付平台的证书）
        if (file_exists($wechatpayCertificateFilePath)) {
            curl_setopt($ch, CURLOPT_CAINFO, $wechatpayCertificateFilePath);
        }

        // 执行请求
        $response = curl_exec($ch);
        $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        // 处理响应
        if ($response === false) {
            throw new \Exception("cURL 请求失败: " . $error);
        }

        if ($statusCode !== 200) {
            throw new \Exception("微信支付请求失败，状态码：{$statusCode}，响应内容：{$response}");
        }

        return [$statusCode, $response];
    }


    private// 解密加密数据
    function decryptData($ciphertext, $associatedData, $nonce, $apiKey)
    {
        $tagLength = 16;
        $tag = substr($ciphertext, -$tagLength);
        $ciphertext = substr($ciphertext, 0, -$tagLength);

        $decryptedData = openssl_decrypt(
            $ciphertext,
            'aes-256-gcm',
            $apiKey,
            OPENSSL_RAW_DATA,
            $nonce,
            $tag,
            $associatedData
        );

        if ($decryptedData === false) {
            $this->error('解密失败：' . openssl_error_string());
        }
        return $decryptedData;
    }

    function wechatPayAppRequest(array $params, string $merchantId, string $merchantSerialNumber, string $merchantPrivateKeyFilePath, string $wechatpayCertificateFilePath): array
    {
        // 加载商户私钥
        $merchantPrivateKey = file_get_contents($merchantPrivateKeyFilePath);
        if ($merchantPrivateKey === false) {
            throw new \Exception("无法读取商户私钥文件");
        }

        // 准备请求URL
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/app';

        // 生成随机字符串
        $nonce = bin2hex(random_bytes(16));

        // 生成时间戳
        $timestamp = time();

        // 构造签名串
        $message = "POST\n/v3/pay/transactions/app\n{$timestamp}\n{$nonce}\n" . json_encode($params) . "\n";

        // 使用私钥对签名串进行签名
        openssl_sign($message, $signature, $merchantPrivateKey, OPENSSL_ALGO_SHA256);
        $signatureBase64 = base64_encode($signature);

        // 构造 Authorization 头
        $authorization = sprintf(
            'WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $merchantId,
            $nonce,
            $timestamp,
            $merchantSerialNumber,
            $signatureBase64
        );

        // 设置请求头
        $headers = [
            'Accept: application/json',
            'Content-Type: application/json',
            'Authorization: ' . $authorization,
            'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', // 必须包含 User-Agent 字段
        ];

        // 初始化 cURL
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
//        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 忽略 SSL 证书验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 忽略主机名验证

        // 设置 CA 证书路径（可选，如果需要验证微信支付平台的证书）
        if (file_exists($wechatpayCertificateFilePath)) {
            curl_setopt($ch, CURLOPT_CAINFO, $wechatpayCertificateFilePath);
        }

        // 执行请求
        $response = curl_exec($ch);
        $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        // 处理响应
        if ($response === false) {
            throw new \Exception("cURL 请求失败: " . $error);
        }

        if ($statusCode !== 200) {
            throw new \Exception("微信支付请求失败，状态码：{$statusCode}，响应内容：{$response}");
        }

        return [$statusCode, $response];
    }


    private
    function generateToken($url, $method, $mchid, $serial, $privateKey)
    {
        $timestamp = time();
        $nonce = bin2hex(random_bytes(16));
        $message = $method . "\n" . parse_url($url, PHP_URL_PATH) . "\n" . $timestamp . "\n" . $nonce . "\n\n";
        openssl_sign($message, $signature, $privateKey, 'sha256WithRSAEncryption');
        $sign = base64_encode($signature);

        return sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $mchid, $nonce, $timestamp, $serial, $sign);
    }

    public
    function wxPayNotify()
    {
        // 配置微信支付参数
        $merchantId = '1682857812'; // 商户号
        $merchantSerialNumber = '224C018B99D3099A85BB363F71BBC2F4D5AC556D'; // 商户证书序列号
        $merchantPrivateKey = PemUtil::loadPrivateKey($_SERVER['DOCUMENT_ROOT'] . '/wxcert/apiclient_key.pem'); // 商户私钥
        $wechatpayCertificate = PemUtil::loadCertificate($_SERVER['DOCUMENT_ROOT'] . '/wxcert/apiclient_cert.pem'); // 微信支付平台证书
        $apiV3Key = 'Zx8kP9qR2sT5vY7wB3mN6cF1hJ4lK0pX'; // API v3密钥

        // 创建微信支付实例
        $instance = Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantSerialNumber,
            'privateKey' => $merchantPrivateKey,
            'certs' => [
                'wechatpay_cert' => $wechatpayCertificate,
            ],
            'options' => [
                'verify' => './httpscert/Nginx/fullchain.pem', // CA 证书路径
            ],
        ]);

        try {
            $inWechatpaySignature = $this->request->header('Wechatpay-Signature');
            $inWechatpayTimestamp = $this->request->header('Wechatpay-Timestamp');
            $inWechatpayNonce = $this->request->header('Wechatpay-Nonce');
            $inBody = file_get_contents('php://input');

            // 检查通知时间偏移量
            $timeOffsetStatus = 300 >= abs(Rsa::timestamp() - (int)$inWechatpayTimestamp);
            if (!$timeOffsetStatus) {
                throw new \Exception('通知时间偏移量超过5分钟');
            }

            // 构造验签名串
            $message = $inWechatpayTimestamp . "\n" . $inWechatpayNonce . "\n" . $inBody . "\n";

            // 验证签名
            $verifiedStatus = Rsa::verify(
                $message,
                $inWechatpaySignature,
                $wechatpayCertificate
            );
            if (!$verifiedStatus) {
                throw new \Exception('签名验证失败');
            }

            // 解密通知数据
            $inBodyArray = (array)json_decode($inBody, true);
            $decryptData = AesGcm::decrypt(
                $inBodyArray['resource']['ciphertext'],
                $apiV3Key,
                $inBodyArray['resource']['nonce'],
                $inBodyArray['resource']['associated_data']
            );
            $decryptDataArray = json_decode($decryptData, true);

            // 处理支付结果
            if ($decryptDataArray['trade_state'] === 'SUCCESS') {
                $orderId = $decryptDataArray['out_trade_no'];
                $transactionId = $decryptDataArray['transaction_id'];

                // 更新订单状态
                Db::name('order')->where('id', $orderId)->update([
                    'status' => 1,
                    'payment_time' => date('Y-m-d H:i:s'),
                    'transaction_id' => $transactionId
                ]);

                // 记录支付日志
                Log::info('微信支付成功: ' . json_encode($decryptDataArray));

                // 返回成功信息给微信服务器
                return json(['code' => 'SUCCESS', 'message' => '成功'])->code(200);
            }
        } catch (\Exception $e) {
            Log::error('微信支付通知处理异常: ' . $e->getMessage());
            return json(['code' => 'FAIL', 'message' => $e->getMessage()])->code(500);
        }
    }


    public
    function wxapppay()
    {
        // 商户号
        $merchantId = '1682857812';
        // 商户API证书序列号
        $merchantSerialNumber = '224C018B99D3099A85BB363F71BBC2F4D5AC556D';
        // 商户私钥文件路径
        $merchantPrivateKeyFilePath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/apiclient_key.pem';
        // 微信支付平台证书文件路径
        $wechatpayCertificateFilePath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/wechatpay_1CCBC1AD439A04D33A713F302466A507B41C347A.pem';
        // 检查文件是否存在且可读
        if (!file_exists($wechatpayCertificateFilePath) || !is_readable($wechatpayCertificateFilePath)) {
            die("微信支付平台证书文件不存在或不可读：$wechatpayCertificateFilePath");
        }
        // 尝试读取证书内容
        $certContent = file_get_contents($wechatpayCertificateFilePath);
        if ($certContent === false) {
            die("无法读取微信支付平台证书文件内容");
        }
        // 检查证书内容
        if (strpos($certContent, '-----BEGIN CERTIFICATE-----') === false) {
            die("微信支付平台证书文件内容格式不正确");
        }
        $rootCaPath = $_SERVER['DOCUMENT_ROOT'] . '/wxcert/rootca.pem';
        // 商户API v3密钥
        $merchantApiV3Key = 'Zx8kP9qR2sT5vY7wB3mN6cF1hJ4lK0pX';
        $appId = 'wx2bab6cd1400f48bb';
        // 读取商户私钥
        $merchantPrivateKey = PemUtil::loadPrivateKey($merchantPrivateKeyFilePath);
        // 读取私钥内容
        $merchantPrivateKey = file_get_contents($merchantPrivateKeyFilePath);
        // 加载微信支付平台证书
        $wechatpayCertificate = PemUtil::loadCertificate($wechatpayCertificateFilePath);
        // 验证私钥是否可以被正确加载
//        $merchantPrivateKeyInstance = openssl_pkey_get_private($merchantPrivateKey);
//        if ($merchantPrivateKeyInstance === false) {
//            throw new \Exception("无法加载私钥，请检查私钥文件格式是否正确");
//        }
        // 如果没有平台证书，可以通过以下代码获取平台证书
        //$this->getPlatformCertificates($rootCaPath,$merchantApiV3Key,$merchantId,$merchantPrivateKey, $merchantSerialNumber, $merchantPrivateKeyFilePath);

        // 读取微信支付平台证书内容
        $wechatpayCertificate = file_get_contents($wechatpayCertificateFilePath);
        if ($wechatpayCertificate === false) {
            throw new \Exception("无法读取微信支付平台证书内容");
        }

        // 解析微信支付平台证书序列号
        $wechatpaySerialNumber = PemUtil::parseCertificateSerialNo($wechatpayCertificate);


// 构建微信支付 API 客户端
        $builder = Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantSerialNumber,
            'privateKey' => $merchantPrivateKey,
            'certs' => [
                $wechatpaySerialNumber => $wechatpayCertificate
            ],
            'apiV3Key' => $merchantApiV3Key
        ]);

// 统一下单参数
        $order = [
            'appid' => $appId,
            'mchid' => $merchantId,
            'description' => '测试商品',
            'out_trade_no' => uniqid(), // 生成唯一的订单号
            'notify_url' => 'https://dev002.gxyushengwenhua.com/ysapi/v1/Enrollment/notify/', // 支付结果通知地址
            'amount' => [
                'total' => 1, // 订单总金额，单位为分
                'currency' => 'CNY'
            ]
        ];

        // 发起统一下单请求
        try {
            //$response = $builder->chain('v3/pay/transactions/app')->post($order);
            [$StatusCode, $response] = $this->wechatPayAppRequest($order, $merchantId, $merchantSerialNumber, $merchantPrivateKeyFilePath, $wechatpayCertificateFilePath);
            if ($StatusCode === 200) {
                $result = json_decode($response, true);
                $prepayId = $result['prepay_id'];

                // 生成 APP 端调起支付所需的参数
                $timestamp = time();
                $nonceStr = uniqid();
                $package = "prepay_id={$prepayId}";
                $signType = 'RSA';

                // 生成签名
                $signData = "{$appId}\n{$timestamp}\n{$nonceStr}\n{$package}\n";
                $privateKey = openssl_pkey_get_private($merchantPrivateKey);
                openssl_sign($signData, $paySign, $privateKey, OPENSSL_ALGO_SHA256);
                $paySign = base64_encode($paySign);

                // 返回给 APP 端的支付参数
                $appPayParams = [
                    'appid' => $appId,
                    'partnerid' => $merchantId,
                    'prepayid' => $prepayId,
                    'package' => $package,
                    'noncestr' => $nonceStr,
                    'timestamp' => $timestamp,
                    'sign' => $paySign,
                    'signType' => $signType
                ];
                echo json_encode($appPayParams);
            } else {
                echo '下单失败：' . $response->getBody();
            }
        } catch (\Exception $e) {
            echo '请求出错：' . $e->getMessage();
        }
    }


    public
    function getSubjectlv()
    {

        $latitude = $this->request->get('latitude', 22.81);
        $longitude = $this->request->get('longitude', 108.35528564453125);
        $classes_id = $this->request->get('classes_id', 0);
        $classes_type = $this->request->get('classes_type', 1);
        $ocation_id = $this->request->get('ocation_id', 0);

        $courseDetails = Db::name('course_table_autoplan')
            ->alias('t')
            ->join('fa_course_table tb', 'tb.id=t.table_id', 'INNER')
            ->join($classes_type == 1 ? 'eb_classes c' : 'fa_ocation_class c',
                $classes_type == 1 ? 'c.id=t.classes_id' : 'c.id = t.classes_id',
                'INNER')
            ->join('fa_ocation o', 'o.id=t.ocation_id', 'left')
            ->join('fa_ocation o2', 'o.pid=o2.id', 'left')
            ->join('fa_subject_lv sl', 'sl.id=c.subject_lv', 'left')
            ->join('fa_address_lv o3', 'sl.addresslv_id=o3.id', 'left')
            ->field("
            tb.name as course_name,
            o2.name as location_name,
            c.intro as description,
            o.id as ocation_id,
            o.address as address,
            c.description as detail_description,
            o.`longitude`,
            o.`latitude`,
            c.price,
            c.rel_type,
            sl.name as subject_name,
            sl.id as subject_lv,
            c.id as classes_id,
            c.classes_type,
            o3.name as address_lv,
            c.num - apply_num as remaining_slots,
            (
                6371 * ACOS(
                    COS(RADIANS($latitude)) * COS(RADIANS(o.latitude)) * COS(RADIANS(o.longitude) - RADIANS($longitude)) +
                    SIN(RADIANS($latitude)) * SIN(RADIANS(o.latitude))
                )
            ) AS distance
        ")->where(function ($query) use ($ocation_id, $classes_id) {
                $query->where('t.ocation_id', $ocation_id)
                    ->whereOr('t.classes_id', $classes_id);
            })
            ->where(['c.classes_type' => $classes_type])
            ->group("t.ocation_id,t.classes_id,sl.id")
            ->order('distance', 'asc') // 按距离升序排序
            ->select();
        Db::name('course_table_autoplan')->getlastsql();
        $subjects = array();
        foreach ($courseDetails as $key => $value) {
            $subjects[] = array('value' => $value['subject_lv'], 'text' => $value['address_lv'] . " " . $value['subject_name'], 'classes_id' => $value['classes_id']);
        }
        // 排序 $courseDetails，将classes_id=$classes_id的课程放在前面
        usort($subjects, function ($a, $b) use ($classes_id) {
            // 如果$a的classes_id等于目标classes_id，排在前面
            if ($a['classes_id'] == $classes_id && $b['classes_id'] != $classes_id) {
                return -1;
            }
            // 如果$b的classes_id等于目标classes_id，排在前面
            if ($b['classes_id'] == $classes_id && $a['classes_id'] != $classes_id) {
                return 1;
            }
            // 其他情况保持原有顺序
            return 0;
        });

        return json(['code' => 200, 'msg' => 'success', 'data' => $subjects]);
    }

    public
    function isApplyed()
    {
        $classes_id = $_REQUEST['classes_id'];
        $classes_type = $_REQUEST['classes_type'] ?? 1;
        $user = $this->auth->getUser(true);
        if (empty($user)) {
            $this->error('未报名', [], 400);
            return false;
        }
        $result = Db::name('eb_apply_form_item')
            ->where(['classes_id' => $classes_id, 'classes_type' => $classes_type, 'user_id' => $user['uid'], 'status' => 1])
            ->find();
        //Db::name('eb_apply_form_item')->getlastsql()
        // 如果$result不为空，则表示已经报名
        if (!empty($result)) {
            $this->success('已报名', $result, 200);
        }
        $this->error('未报名', [], 400);
    }

    public
    function downloadCert()
    {
        // 商户相关配置
        $merchantId = '1682857812'; // 商户号
        $merchantSerialNumber = '224C018B99D3099A85BB363F71BBC2F4D5AC556D'; // 商户证书序列号
        $merchantPrivateKey = PemUtil::loadPrivateKey('./wxcert/apiclient_key.pem'); // 商户私钥
        $apiV3Key = 'Zx8kP9qR2sT5vY7wB3mN6cF1hJ4lK0pX'; // API v3密钥

// 创建一个临时的微信支付实例，用于下载证书
        $instance = Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantSerialNumber,
            'privateKey' => $merchantPrivateKey,
            'certs' => [

            ],
            'options' => [
                'verify' => false, // 临时禁用SSL验证，仅用于下载证书
            ],
        ]);

// 获取平台证书列表
        $certificates = $instance->chain('v3/certificates')->get();
        $certificates = json_decode($certificates->getBody(), true);

        foreach ($certificates['data'] as $cert) {
            $serial = $cert['serial_no'];
            $certStr = AesGcm::decrypt($cert['encrypt_certificate']['ciphertext'], $apiV3Key, $cert['encrypt_certificate']['nonce'], $cert['encrypt_certificate']['associated_data']);

            // 保存证书到文件
            file_put_contents("/path/to/wechatpay_cert_{$serial}.pem", $certStr);
            echo "Downloaded certificate: {$serial}\n";
        }

        echo "Certificates download completed.\n";
    }


    function getPlatformCertificates($rootCaPath, $merchantApiV3Key, $merchantId, $merchantPrivateKey, $merchantSerialNumber, $merchantPrivateKeyFilePath)
    {
        // 如果没有平台证书，可以通过以下代码获取平台证书
        $cert = file_get_contents($rootCaPath);
        $certInfo = openssl_x509_parse($cert);
        if ($certInfo === false) {
            echo "无法解析证书文件";
        } else {
            echo "证书文件有效";
        }
        // 创建一个临时的实例来下载证书
        $instanceForCert = \WeChatPay\Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantSerialNumber,
            'privateKey' => $merchantPrivateKey, // 使用私钥内容而不是文件路径
            'certs' => [
                'any_string_makes_sense' => $merchantPrivateKey, // 使用私钥内容
            ],
            'httpClient' => [
                'verify' => $rootCaPath, // 指定根证书路径
            ],
        ]);
        try {
            // 调用获取平台证书接口
            // 读取私钥
            $privateKey = file_get_contents($merchantPrivateKeyFilePath);
            // 初始化cURL会话
            $ch = curl_init();
            // 设置URL
            curl_setopt($ch, CURLOPT_URL, 'https://api.mch.weixin.qq.com/v3/certificates');
            // 设置为GET请求
            curl_setopt($ch, CURLOPT_HTTPGET, true);
            // 返回结果，而不是直接输出
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            // SSL验证设置
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
            // 如果有CA证书，设置CA证书路径
            if (file_exists($rootCaPath)) {
                curl_setopt($ch, CURLOPT_CAINFO, $rootCaPath);
            }
            // 设置HTTP头
            $timestamp = time();
            $nonce = substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'), 0, 32);
            $message = "GET\n/v3/certificates\n{$timestamp}\n{$nonce}\n\n";
            openssl_sign($message, $signature, $privateKey, 'sha256WithRSAEncryption');
            $sign = base64_encode($signature);
            $headers = [
                'Accept: application/json',
                'Authorization: WECHATPAY2-SHA256-RSA2048 ' . implode(',', [
                    "mchid=\"{$merchantId}\"",
                    "nonce_str=\"{$nonce}\"",
                    "timestamp=\"{$timestamp}\"",
                    "serial_no=\"{$merchantSerialNumber}\"",
                    "signature=\"{$sign}\""
                ]),
                'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
            ];
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            // 启用详细的cURL错误报告
            curl_setopt($ch, CURLOPT_VERBOSE, true);
            $verbose = fopen('php://temp', 'w+');
            curl_setopt($ch, CURLOPT_STDERR, $verbose);
            // 执行请求
            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            // 检查是否有错误发生
            if ($response === false) {
                $error = curl_error($ch);
                $errorNo = curl_errno($ch);
                echo "cURL Error ({$errorNo}): {$error}\n";
                // 输出详细的错误信息
                rewind($verbose);
                $verboseLog = stream_get_contents($verbose);
                echo "Verbose information:\n", htmlspecialchars($verboseLog), "\n";
            } else {
                // 处理响应
                if ($httpCode === 200) {
                    $responseData = json_decode($response, true);
                    if (isset($responseData['data']) && is_array($responseData['data'])) {
                        foreach ($responseData['data'] as $certificate) {
                            $serialNo = $certificate['serial_no'];
                            $certEncrypted = $certificate['encrypt_certificate'];
                            $certDecrypted = \WeChatPay\Crypto\AesGcm::decrypt(
                                $certEncrypted['ciphertext'],
                                $merchantApiV3Key,
                                $certEncrypted['nonce'],
                                $certEncrypted['associated_data']
                            );
                            // 保存证书到文件
                            $certPath = $_SERVER['DOCUMENT_ROOT'] . "/wxcert/cert_{$serialNo}.pem";
                            if (file_put_contents($certPath, $certDecrypted)) {
                                echo "证书 {$serialNo} 已保存到 {$certPath}\n";
                            } else {
                                echo "保存证书 {$serialNo} 失败\n";
                            }
                        }
                    } else {
                        echo "响应数据格式不正确\n";
                    }
                } else {
                    echo "HTTP 请求失败，状态码：{$httpCode}\n";
                    echo "响应内容：{$response}\n";
                }
            }
            //openssl_free_key($merchantPrivateKeyInstance);

        } catch (\Exception $e) {
            echo "错误：" . $e->getMessage() . "\n";
        }
    }

    // Native支付下单
    public function native(request $request)
    {
        if ($_REQUEST["user_id"] > 0) {
            $user = Db::name('fa_user')->where('id', $_REQUEST["user_id"])->find();
        } else {
            $user = $this->auth->getUser();
        }
        if (!$user) {
            return json(['code' => 401, 'msg' => '未登录']);
        }

        $order_id = $request->param('order_id');
        $order = Db::name('fa_order')
            ->alias("o")
            ->join("eb_classes c", 'c.id=o.classes_id', "left")
            ->join("fa_user u", 'u.id=o.user_id', "left")
            ->where('o.id', $order_id)
            ->field("u.username,c.name as class_name,o.*")
            ->find();

        if (!$order) {
            return json(['code' => 404, 'msg' => '订单不存在']);
        }

        // 检查订单状态，避免重复支付
        if ($order['pay_status'] == 1) {
            return json(['code' => 400, 'msg' => '订单已支付']);
        }

        // 获取用户openid（假设已存储在用户表中）
        $openid = $user['openid'];

        if (!$openid) {
            return json(['code' => 403, 'msg' => '未绑定微信账号']);
        }

        function generateOrderNo($userId)
        {
            return date('YmdHis') . str_pad($userId, 6, '0', STR_PAD_LEFT) . mt_rand(1000, 9999);
        }


        // 配置信息
        $mchid = config('wechatpay.mchid');
        $appid = config('wechatpay.appid');
        $serial_no = config('wechat.pay.mchSerialNo');
        $private_key_path = config('wechatpay.merchant_private_key_path');
        $notify_url = config('wechatpay.notify_url');
        $amount = $order['amount'];
        $orderNo = date('YmdHis') . mt_rand(1000, 9999);
        // 读取私钥
        $private_key = file_get_contents($private_key_path);

        // 构建请求数据
        $requestData = [
            'appid' => config('wechatpay.appid'),
            'mchid' => config('wechatpay.mchid'),
            'out_trade_no' => $orderNo,
            'description' => "商品支付{$order['class_name']}-{$order['username']}",
            'notify_url' => config('wechatpay.notify_url'),
            'amount' => [
                'total' => intval($amount),
                'currency' => 'CNY'
            ],
            'scene_info' => [
                'payer_client_ip' => $this->request->ip()
            ]
        ];

        // 生成签名
        $timestamp = time();
        $nonceStr = uniqid();
        $signature = $this->generateV3Sign('POST', '/v3/pay/transactions/native', $timestamp, $nonceStr, $requestData);

        // 请求微信支付
        $result = $this->requestWechatPay('/v3/pay/transactions/native', $requestData, $timestamp, $nonceStr, $signature);

        if (isset($result['code_url'])) {
            $data = array();
            $data['order_sn'] = $orderNo;
            Db::name('fa_order')->where('id', $order_id)->update($data);
            $data['code_url'] = $result['code_url'];
            $data['total_fee'] = $order['amount'] * 100;
            $data['trade_state'] = 'NOTPAY';
            $data['time_expire'] = date('Y-m-d H:i:s', time() + 60 * 30);
            $data['order_id'] = $order_id;
            $this->success('下单成功', $data, ['code_url' => $result['code_url']]);
        } else {
            $this->error($result['message'] ?? '下单失败');
        }
    }

    // 生成V3签名
    private function generateV3Sign($method, $url, $timestamp, $nonceStr, $body)
    {
        $message = $method . "\n" .
            $url . "\n" .
            $timestamp . "\n" .
            $nonceStr . "\n" .
            json_encode($body) . "\n";
        $private_key_path = "d:\\wwwroot\\www\\dev002.gxyushengwenhua.com\\cert\\apiclient_key.pem";
        $privateKey = file_get_contents($private_key_path);
        openssl_sign($message, $signature, $privateKey, 'sha256WithRSAEncryption');

        return base64_encode($signature);
    }

    // 请求微信支付接口
    private function requestWechatPay($path, $data, $timestamp, $nonceStr, $signature)
    {
        $url = 'https://api.mch.weixin.qq.com' . $path;

        $headers = [
            'Authorization: WECHATPAY2-SHA256-RSA2048 ' .
            'mchid="' . config('wechatpay.mchid') . '",' .
            'nonce_str="' . $nonceStr . '",' .
            'timestamp="' . $timestamp . '",' .
            'serial_no="' . config('wechatpay.serial_no') . '",' .
            'signature="' . $signature . '"',
            'Accept: application/json',
            'Content-Type: application/json',
            'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
        ];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

        $response = curl_exec($ch);
        curl_close($ch);

        return json_decode($response, true);
    }

    // 支付结果通知
    public function native_notify()
    {
        $rawData = file_get_contents('php://input');
        $data = json_decode($rawData, true);


        // 记录日志
        $logDir = $_SERVER["DOCUMENT_ROOT"] . '/paylog/' . date("Ymd") . "/";
        if (!file_exists($logDir)) {
            mkdir($logDir, 0777, true);
        }
        $logFile = $logDir . 'wxpay_notify_' . date('His') . '.txt';
        $postData = file_get_contents('php://input');
        $headers = getallheaders();
        $logContent = "POST https://dev002.gxyushengwenhua.com/ysapi/v1/enrollment/notify/?XDEBUG_SESSION_START=54325 HTTP/1.1\n";
        foreach ($headers as $name => $value) {
            $logContent .= "$name: $value\n";
        }
        $logContent .= "\n\n";
        $logContent .= $postData;
        file_put_contents($logFile, $logContent);

        if ($_SERVER["HTTP_HOST"] != 'ys.yangjc.com') {
            // 复制一份请求参数，模拟POST换个域名转发到 ys.yangjc.com,请求头也需要复制
            $headers = getallheaders();
            $headersStr = '';
            foreach ($headers as $name => $value) {
                if ($name !== 'Host') {
                    $headersStr .= "$name: $value\n";
                }
            }
            $headersStr .= "Host: ys.yangjc.com\n";
            // 将headers字符串转换为数组
            $headersArray = explode("\n", trim($headersStr));
            $headersArray = array_filter($headersArray); // 移除空元素
            $url = 'https://ys.yangjc.com/ysapi/v1/enrollment/notify/?noreturn=1';
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headersArray);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exec($ch);
            curl_close($ch);
            // 记录日志
            $logContent = "\r\n\r\n\r\nPOST https://ys.yangjc.com/ysapi/v1/enrollment/notify/ HTTP/1.1\n";
            $logContent .= $headersStr;
            $logContent .= "\n\n";
            $logContent .= $postData;
            //追加方式写入日志
            file_put_contents($logFile, $logContent, FILE_APPEND);
        }

        // 验证签名
        if ($this->verifySign($rawData)) {
            // 确保是支付成功通知
            if ($data['event_type'] !== 'TRANSACTION.SUCCESS') {
                http_response_code(400);
                echo json_encode(['code' => 'FAIL', 'message' => '不是支付成功通知']);
                exit;
            }
            //$apiv3_key= '7aB3cD9eF1gH2iJ4kL6mN8oP0qR2sT4u';
            //$apiv3_key='Zx8kP9qR2sT5vY7wB3mN6cF1hJ4lK0pX';
            // 解密 resource 字段
            $resource = $data['resource'];
            $decrypted = $this->decryptResource(
                $resource['ciphertext'],
                $resource['nonce'],
                $resource['associated_data'],
                config('wechatpay.apiv3_key')
            );
        } else {
            $response = [
                'code' => 'FAIL',
                'message' => '签名验证失败'
            ];
            return json($response);
        }

        return json($decrypted);
    }

    protected function verifySign($data)
    {
        try {
            // 获取微信支付平台证书
            $publicKey = file_get_contents(config('wechatpay.platform_cert'));
            //$apiV3Key = config('wechatpay.apiv3_key');
            //$apiV3Key = 'Zx8kP9qR2sT5vY7wB3mN6cF1hJ4lK0pX';
            $apiV3Key = '7aB3cD9eF1gH2iJ4kL6mN8oP0qR2sT4u';
            // 从header中获取签名信息
            $signature = $this->request->header('Wechatpay-Signature');
            $timestamp = $this->request->header('Wechatpay-Timestamp');
            $nonce = $this->request->header('Wechatpay-Nonce');
            $serialNo = $this->request->header('Wechatpay-Serial');

            // 验证时间戳有效性（5分钟内）
//            if (abs(time() - $timestamp) > 300) {
//                Log::error('微信支付签名验证失败：时间戳过期');
//                return false;
//            }

            // 构建验签字符串
            $message = "{$timestamp}\n{$nonce}\n{$data}\n";

            // 验证签名
            $result = openssl_verify(
                $message,
                base64_decode($signature),
                $publicKey,
                'sha256WithRSAEncryption'
            );

            return $result === 1;
        } catch (\Exception $e) {
            Log::error('微信支付签名验证异常：' . $e->getMessage());
            return false;
        }
    }

    // 解密回调数据的辅助函数
    private function decryptResource($ciphertext, $nonce, $associatedData, $key)
    {

        //$key="Zx8kP9qR2sT5vY7wB3mN6cF1hJ4lK0pX";
        // Base64解码密文
        $ciphertext0 = $ciphertext;
        $ciphertext = base64_decode($ciphertext);

        // 检查密文长度是否足够
        if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
            Log::error('微信支付解密失败：密文长度不足');
            return false;
        }
        // ext-sodium (default installed on >= PHP 7.2)
        if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) {
            $decryptedData = \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonce, $key);
            if ($decryptedData === false) {
                Log::error('微信支付解密失败：sodium 解密失败');
                return false;
            }
            return $this->processDecryptedData($decryptedData);
        }

        // ext-libsodium (need install libsodium-php 1.x via pecl)
        if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
            $decryptedData = \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonce, $key);
            if ($decryptedData === false) {
                Log::error('微信支付解密失败：libsodium 解密失败');
                return false;
            }
            return $this->processDecryptedData($decryptedData);
        }


        // 加密文本消息解密
        //$inBodyResource = AesGcm::decrypt($ciphertext0, $key, $nonce, $associatedData);
        // 把解密后的文本转换为PHP Array数组
        //$inBodyResourceArray = (array)json_decode($inBodyResource, true);

        // openssl (PHP >= 7.1 support AEAD)
        if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
            $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
            $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
            $decryptedData = \openssl_decrypt($ctext, 'aes-256-gcm', $key, \OPENSSL_RAW_DATA, $nonce, $authTag, $associatedData);
            if ($decryptedData === false) {
                Log::error('微信支付解密失败：openssl 解密失败');
                return false;
            }
            return $this->processDecryptedData($decryptedData);
        }


    }

    private function processDecryptedData($decryptedData)
    {
        $decryptedData = json_decode($decryptedData, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            Log::error('微信支付解密失败：JSON 解码失败');
            return false;
        }
        // 下单成功，修改订单状态
        $orderId = $decryptedData['out_trade_no'];
        //$orderId="202504201143237053_21_22_23";
        $ids = explode('_', $orderId);
        $ids = array_slice($ids, 1);
        $pay_time = date("Y-m-d H:i:s", strtotime($decryptedData['success_time']));
        Db::name('fa_order')
            ->whereIn('id', $ids)
            ->update([
                'pay_status' => 1,
                'status' => 1,
                'payment_status' => 1,
                'total_fee' => $decryptedData['amount']['total'] / 100,
                'pay_time' => strtotime($decryptedData['success_time']),
                'pay_type' => 'wechat_' . $decryptedData['trade_type'],
                'pay_channel_id' => $decryptedData['transaction_id'],
                'pay_channel_extra' => $decryptedData['payer']['openid'],
                'pay_channel_order_id' => $decryptedData['out_trade_no'],
                'pay_channel_order_sn' => $decryptedData['out_trade_no'],
                'pay_channel' => $decryptedData['trade_state_desc'],
            ]);
        if (!empty($ids)) {
            Db::name('eb_apply_form_item')
                ->whereIn('order_id', $ids)
                ->update([
                    'pay_status' => 1,
                    'pay_time' => $pay_time,
                ]);
        }
        $noreturn = $this->request->param('noreturn', 0);
        if ($noreturn) {
            return true;
        }
        echo '{"code": "SUCCESS",
            "message": "OK"
        }';
        exit;

    }


//清除 eb_apply_form_item
    public function clear()
    {
        $sql = "truncate table eb_apply_form_item;";
        Db::execute($sql);
        $this->success('清除成功');
    }

    /**
     * 二次校验接口
     *
     * @ApiMethod (POST)
     * @param string $appId
     * @param string $timeStamp
     * @param string $nonceStr
     * @param string $package
     * @return void
     */
    public function verify()
    {
        $appId = $this->request->post('appId');
        $timeStamp = $this->request->post('timeStamp');
        $nonceStr = $this->request->post('nonceStr');
        $package = $this->request->post('package');
        $paySign = $this->request->post('paySign');

        // 3. 验证 paySign 签名
        // 这里您需要使用相同的签名算法和密钥，验证 paySign 是否正确
        // 示例：使用 RSA 签名验证
        $stringA = "appId=$appId&timeStamp=$timeStamp&nonceStr=$nonceStr&package=$package";

        // 请替换为您的公钥
        //$publicKey = "-----BEGIN PUBLIC KEY-----\n" . config('site.wechat_pay_public_key') . "\n-----END PUBLIC KEY-----";
        $publicKey = file_get_contents(config('wechatpay.platform_cert'));
        $res = openssl_verify($stringA, base64_decode($paySign), openssl_pkey_get_public($publicKey), OPENSSL_ALGO_SHA256);

        if ($res !== 1) {
            $this->error(__('签名验证失败'));
        }

        // 4. 验证 timeStamp 是否在有效期内
        $timeDiff = abs(time() - $timeStamp);
        if ($timeDiff > 300) { // 5 分钟有效期
            $this->error(__('timeStamp 已过期'));
        }

        // 5. 验证成功
        $this->success(__('验证成功'));
    }


    //教学点列表
    public function teaching_point_list()
    {
        $latitude = $this->request->get('latitude', 22.81);
        $longitude = $this->request->get('longitude', 108.35528564453125);

        // 获取教学点数据
        $courseDetails = Db::name('fa_ocation')
            ->field("
            id,
            pid,
            name,
            address,
            rel_type,
            longitude,
            latitude,
            address_lv_octionlevel,
            status,
            (
                6371 * ACOS(
                    COS(RADIANS($latitude)) * COS(RADIANS(latitude)) * COS(RADIANS(longitude) - RADIANS($longitude)) +
                    SIN(RADIANS($latitude)) * SIN(RADIANS(latitude))
                )
            ) AS distance
        ")
            ->where('status', 1)
            ->order('distance', 'asc') // 按距离升序排序
            ->select();

        // 使用Tree类构建树状结构
        $tree = \fast\Tree::instance();
        $tree->init($courseDetails, 'pid');
        $pid = 0;
        $arr = $tree->getTreeList($tree->getTreeArray($pid), 'name');

        $data = array();
        for ($i = 0; $i < count($arr); $i++) {
            $v = $arr[$i];

            // 确保level字段存在，如果不存在则设置为0
            $level = isset($v['level']) ? $v['level'] : 0;

            // 根据层级添加不同的样式
            if ($level == 0) {
                $v['name'] = "{$v['name']}";
            }
            if ($level == 1) {
                $v['name'] = preg_replace_callback("/^(\&nbsp\;)((?:\s|├|\&nbsp\;|│|├|└)*?)(.*)$/Uisx", function ($em) use ($v) {
                    return "{$em[3]}";
                }, $v['name']);
            }
            if ($level == 2) {
                $v['name'] = preg_replace_callback("/^(\&nbsp\;)((?:\s|\&nbsp\;|\│|\├|└)*?)(.*)$/Uisx", function ($em) use ($v) {
                    return "{$em[3]}";
                }, $v['name']);
            }
            if ($level == 3) {
                $v['name'] = preg_replace_callback("/^(\&nbsp\;)((?:\s|\&nbsp\;|\│|\├|└)*?)(.*)$/Uisx", function ($em) use ($v) {
                    return "{$em[3]}";
                }, $v['name']);
            }
            if ($level == 4) {

            }

            $data[] = [
                'id' => $v['id'],
                'parent' => $v['pid'] ? $v['pid'] : '#',
                'name' => preg_replace("/\&nbsp\;/i", "", $v['name']),
                'level' => $level,
                'address' => $v['address'],
                'rel_type' => $v['rel_type'],
                'longitude' => $v['longitude'],
                'latitude' => $v['latitude'],
                'address_lv_octionlevel' => $v['address_lv_octionlevel'],
                'status' => $v['status'],
                'distance' => $v['distance'],
                'isCollected' => $this->isCollected($v['id'], 1),
            ];
        }

        return json(['code' => 200, 'msg' => 'success', 'data' => $data]);
    }

    //教学点活动，包括 教学课程、社区课程、校外课程
    public
    function getOcationAllClass()
    {

        $ocationId = input('ocation_id/d');

        if (!$ocationId) {
            return json(['code' => 400, 'msg' => '缺少教学点 ID']);
        }
        $data_temp = Db::name('Ocation')->alias('o')
            ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER')
            ->field('o.id,o.name,o.address,o.`longitude`,o.`latitude`,o.image')
            ->where('o.id', $ocationId)->select();
        $data = $data_temp[0];
        // 查找教学点的图片，没有的话，就以其中一个班的封面做为封面
        if ($data['image'] == null) {
            $data['image'] = Db::name('course_table_autoplan_temp')
                ->alias('t')
                ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
                ->where('t.ocation_id', $ocationId)
                ->value('c.cover');
        }
        if ($data['image'] == null) {
            $data['image'] = Db::name('course_table_autoplan_temp')
                ->alias('t')
                ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
                ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER')
                ->join('fa_ocation o2', 'o2.id=t.ocation_id', 'INNER')
                ->where('o2.id', $ocationId)
                ->value('c.cover');
        }
        if ($data['image'] == null) {
            $data['image'] = "/uploads/20250304/d9ad41727f2d423a38d4860c16df76ea.jpg";
        }


        $courseDetails = Db::name('course_table_autoplan')
            ->alias('t')
            ->join('fa_course_table tb', 'tb.id=t.table_id', 'INNER')
            ->join('eb_classes c', 'c.id=t.classes_id', 'INNER')
            ->join('fa_ocation o', 'o.id=t.ocation_id', 'INNER')
            ->join('fa_ocation o2', 'o.pid=o2.id', 'INNER')
            ->join('fa_subject_lv sl', 'sl.id=c.subject_lv', 'INNER')
            ->join('eb_apply_form_item a', "a.classes_id=c.id", 'LEFT')
            ->field("
            o.id as ocation_id,
            o.name as ocation_name,
            tb.name as course_name,
            o2.name as parent_location_name,
            o2.id as parent_location_id,
            c.intro as description,
            o.address as address,
            c.description as detail_description,
            o.`longitude`,
            o.`latitude`,
            c.price,
            c.intro,
            c.rel_type,
            c.id as classes_id,
            c.name as classes_name,
            sl.name as subject_name,
            sl.id as subjectlv_id,
            a.id as apply_id,
            c.num - apply_num as remaining_slots
        ")
            ->where('o2.id', $ocationId)
            ->whereOr('o.id', $ocationId)
            ->group("t.ocation_id,t.classes_id")
            ->select();

        $data["courseList"] = $courseDetails;
        return json([
            'code' => 200,
            'msg' => 'success',
            'data' => $data
        ]);

    }

    public function collectOcation($ocation_id, $school_id)
    {
        if (empty($school_id)) {
            return $this->error('请传学校ID');
        }
        $user = $this->auth->getUser(true);

        if (empty($user)) {
            return $this->error('请先登录');
        }
        $params = [
            'user_id' => $user['uid'],
            'target_id' => $ocation_id,
            'school_id' => $school_id,
            'type' => 1
        ];

        return $this->success('收藏成功', empty(Favorites::where($params)->where('delete_time', null)->find()) ? Favorites::create($params) : null);
    }

    public function isCollected($ocation_id, $collect_type = 0)
    {
        $user = $this->auth->getUser(true);
        if (!$user) {
            return false;
        }
        $user_id = $user['uid'];
        try {
            $result = Db::name('favorites')->where(['user_id' => $user_id, 'type' => $collect_type, 'target_id' => $ocation_id, 'delete_time' => null])->find();
            if ($result) {
                return true;
            } else {
                return false;
            }
        } catch (Exception $e) {
            return false;
        }
    }

    public function getRegistration(Request $request)
    {
        $user = $this->auth->getUser(true);

        if (empty($user['uid'])) {
            return $this->error('请先登录', [], 401);
        }
        $params = $request->param();
        //$params['courses']="[{&quot;classes_id&quot;:99,&quot;classes_type&quot;:1},{&quot;classes_id&quot;:108,&quot;classes_type&quot;:1}]"
        //将&quot;符号解释成"
        if (isset($params['table_id']) && is_numeric($params['table_id']) && $params['table_id']) {
            $params['table_id'] = intval($params['table_id']);
            $rows = Db::name('registration')->where('user_id', $user['uid'])->where('table_id', '=', $params['table_id'])->select();
            header('Content-Type: application/json');
            if (empty($rows)) {
                return $this->error('请先选择课程、填写报名表和手签板', null, 400);
            } else {
                return $this->success('返回成功', $rows[0]);
            }
        } else {
            $params['courses'] = json_decode(htmlspecialchars_decode($params['courses'], ENT_QUOTES), true);
            for ($i = 0; $i < count($params['courses']); $i++) {
                $lists = $this->getTableReportByClassesId($params['courses'][$i]['classes_id']);
                $table_id = $lists['table_id'];
                $apply_form = db::name("eb_apply_form")->where("table_id", $table_id)->where("status", 1)->find();
                $apply_form_id = $apply_form['id'];
                Db::name('registration')->where('table_id', $table_id)->where('user_id', $user['uid'])->update(['apply_form_id' => $apply_form_id]);
                //查找registration表中是否存在table_id，若存在则返回registration表中的数据，若不存在则返回空
                $rows = Db::name('registration')->where('user_id', $user['uid'])->where('table_id', 'in', $table_id)->select();
                header('Content-Type: application/json');
                if (empty($rows)) {
                    return $this->error('请先选择课程、填写报名表和手签板', null, 400);
                } else {
                    for ($i2 = 0; $i2 < count($rows); $i2++) {
                        //发现有一条数据的id_card或signature为空
                        if ($rows[$i2]['id_card'] != null || $rows[$i2]['signature'] != null) {
                            $rows2 = Db::name('eb_apply_form')->where('table_id', 'in', $table_id)->find();
                            $rows[$i2]["terms_content"] = $this->format_content($rows2["terms_content"]);
                            return $this->success('返回成功', $rows[$i2]);
                            //return json_encode("code:200,msg:success,data:{$rows[$i2]}");
                            break;
                        }

                    }
                }
            }

            $form = Db::name('eb_apply_form')->where('status', 1)->order('id', 'asc')->find();
            $rows = Db::name('registration')->where('user_id', $user['uid'])->where('table_id', 'in', $form['table_id'])->select();
            $rows2 = Db::name('eb_apply_form')->where('table_id', 'in', $table_id)->find();
            $rows[0]["terms_content"] = $this->format_content($rows2["terms_content"]);
            return $this->success('返回成功', $rows[0]);

        }

        return $this->success('返回成功', $rows[0]);
    }

    function format_content($content)
    {
        // 确保参数不为null，如果是null则转换为空字符串
        if ($content === null || $content === '') {
            return '';
        }
        $processedContent = (string)$content;

        // 首先清理内容，保留基本标签
        $processedContent = preg_replace("/<style[^>]*>.*<\/style>/Uisx", "", $processedContent);
        $processedContent = strip_tags($processedContent, "<p><br><b><img><h1><h2><h3><h4><h5><h6><strong><b>");
        $processedContent = preg_replace("/<(?!img)([a-zA-Z]+)\s.*>/Uisx", "<\\1>", $processedContent);
        //每一对换行符中间的换行符替换为空格
        $processedContent = preg_replace('/\n+/', ' ', $processedContent);

        // 为p标签添加样式，但排除以一、二、三...等数字开头的段落
        $processedContent = preg_replace_callback(
            '/<p>(.*?)<\/p>/is',
            function ($matches) {
                $pContent = $matches[1];

                // 检查段落内容是否以一、二、三、四、五、六、七、八、九、十等数字开头
                if (preg_match('/^\s*[一二三四五六七八九十]+[、\.\s]/u', $pContent)) {
                    // 数字开头的段落不缩进
                    return '<p style="font-size: 18px; color: #000; line-height: 1.6; margin: 10px 0; line-height: 1.5;">' . $pContent . '</p>';
                } else {
                    // 普通段落缩进2个字符
                    return '<p style="font-size: 18px; color: #000; line-height: 1.6; margin: 10px 0; text-indent: 2em; line-height: 1.5;">' . $pContent . '</p>';
                }
            },
            $processedContent
        );

        // 提取第一行作为标题，通常是第一行
        //第一对<p>标签之间的内容作为标题将其包装为h1标题,且除html处理后的内容不为空
        if (preg_match('/<p[^>]*>(.*?)<\/p>/i', $processedContent, $matches)) {
            $firstLine = trim($matches[1]);
            if ($firstLine && !preg_match('/^\s*<[^>]+>/', $firstLine)) {
                $processedContent = '<h1 style="font-size: 22px; color: #333; font-weight: bold; text-align: center; margin: 20px 0; line-height: 1.5;">' . htmlspecialchars($firstLine) . '</h1>' . "\n" . $processedContent;
            }
        }

        // 在内容头部添加样式表
        $styleTag = '<style>
            p {
                text-indent: 2em !important;
                margin: 10px 0 !important;
                line-height: 1.6 !important;
            }
            h1 {
                font-size: 22px !important;
                color: #333 !important;
                font-weight: bold !important;
                text-align: center !important;
                margin: 20px 0 !important;
                line-height: 1.5 !important;
            }
            h2 {
                font-size: 18px !important;
                color: #000 !important;
                font-weight: bold !important;
                margin: 15px 0 10px !important;
                line-height: 2.5 !important;
            }
        </style>' . "\n";

        $processedContent = $processedContent;

        // 使用正则表达式匹配从段落头部开始到h1标签的部分
        $processedContent = preg_replace(
            '/<h1([^>]*)>/im',
            '<h1$2 style="font-size: 22px; color: #333; font-weight: bold; text-align: center; margin-left: -50px;margin-top: 20px;margin-bottom: 20px; line-height: 1.5;">',
            $processedContent
        );

        // 确保preg_replace返回的是字符串，而不是null
        if ($processedContent === null) {
            $processedContent = (string)$content;
        }

        // 为h2标签添加样式
        $processedContent = preg_replace(
            '/<h2([^>]*)>/i',
            '<h2$1 style="font-size: 18px; color: #000; font-weight: bold; margin: 15px 0 10px; line-height: 2.5;">',
            $processedContent
        );

        // 确保preg_replace返回的是字符串，而不是null
        if ($processedContent === null) {
            $processedContent = (string)$content;
        }

        return $processedContent;
    }

    public function setRegistration(Request $request)
    {
        $user = $this->auth->getUser();

        if (empty($user['uid'])) {
            return $this->error('请先登录', [], 401);
        }

        $params = $request->param();

        $rule = [
            'name' => 'require',
            'age' => 'require|number',
            'sex' => 'require|number',
            'id_card' => 'require'
        ];

        $msg = [
            'name.require' => '姓名不为空',
            'age.require' => '年龄不为空',
            'sex.require' => '性别不为空',
            'id_card.require' => '身份证不为空'
        ];


        $validate = new Validate($rule, $msg);
        $result = $validate->check($params);

        if (!$result) {
            return $this->error('星号不为空，请重新提交', [], 400);
        }

        //courses: [{classes_id: 99, classes_type: 1}, {classes_id: 108, classes_type: 1}]
        //遍历courses，判断classes_type是否为1，若为1则根据classes_id找出其table_id，判断table_id是否存在于registration表中，若不存在则插入，若存在则更新registration表
        foreach ($params['courses'] as $course) {
            if ($course['classes_type'] == 1) {
                $table_id = Db::name('eb_classes')->alias("c")->join("fa_course_table_autoplan t", "t.classes_id=c.id", 'left')->where('c.id', $course['classes_id'])->value('table_id');
                if (Db::name('registration')->where('user_id', $user['uid'])->where('table_id', $table_id)->find()) {
                    $params['updatetime'] = datetime(time());
                    unset($params['courses']);
                    Db::name('registration')->where('user_id', $user['uid'])->where('table_id', $table_id)->update($params);
                } else {
                    $params['createtime'] = datetime(time());
                    $params['table_id'] = $table_id;
                    $params['user_id'] = $user['uid'];
                    unset($params['courses']);
                    Db::name('registration')->insert($params);
                }
            }
        }
        $this->success('提交成功', $params, 200);
    }

    public function setAgreement(Request $request)
    {
        $user = $this->auth->getUser();

        if (empty($user['uid'])) {
            return $this->error('请先登录', [], 401);
        }

        $params = $request->param();

        if (empty($params['signature'])) {
            return $this->error('签名失败，请从新签名', [], 400);
        }

        if (empty($params['agreed'])) {
            return $this->error('请勾选已阅读用户协议', [], 400);
        }
        if ($params['table_id']) {
            $params['table_id'] = intval($params['table_id']);
            unset($params['courses']);
            Db::name('registration')->where('user_id', $user['uid'])->where('table_id', $params['table_id'])->update($params);
        } else {
            foreach ($params['courses'] as $course) {
                if ($course['classes_type'] == 1) {
                    $lists = $this->getTableReportByClassesId($course['classes_id']);
                    $table_id = $lists['table_id'];
                    if (Db::name('registration')->where('user_id', $user['uid'])->where('table_id', $table_id)->find()) {
                        $params['updatetime'] = datetime(time());
                        if (is_numeric($table_id) && $table_id > 0) $params['table_id'] = $table_id;
                        unset($params['courses']);
                        Db::name('registration')->where('user_id', $user['uid'])->where('table_id', $table_id)->update($params);
                    } else {
                        $params['createtime'] = datetime(time());
                        $params['table_id'] = $table_id;
                        $params['user_id'] = $user['uid'];
                        unset($params['courses']);
                        Db::name('registration')->insert($params);
                    }
                }
            }
        }

        $this->success('签名成功', $params, 200);
    }

    public function getPaymentRecord()
    {
        $user = $this->auth->getUser();

        if (empty($user['uid'])) {
            return $this->error('请先登录', [], 401);
        }

        // 首先检查并更新超过3小时未支付的报名记录状态为5（超时不支付不可见）
        $timeoutTime = date('Y-m-d H:i:s', strtotime('-3 hours'));
        Db::table('eb_apply_form_item')
            ->where('user_id', $user['uid'])
            ->where('status', 1)
            ->where('pay_status', 0)
            ->where('pay_time', null)
            ->where('create_time', '<', $timeoutTime)
            ->update(['status' => 5]);

        $data = Db::table('eb_apply_form_item')->alias('a')
            ->join('(SELECT user_id, classes_id, MAX(id) as max_id FROM eb_apply_form_item WHERE user_id = ' . $user['uid'] . ' AND status = 1 AND order_id > 0 AND pay_status = 0 AND pay_time IS NULL GROUP BY user_id, classes_id) max_items', 'a.user_id = max_items.user_id AND a.classes_id = max_items.classes_id AND a.id = max_items.max_id', 'INNER')
            ->join('eb_classes b', 'b.id=a.classes_id', 'left')
            ->join('fa_ocation c', 'c.id=a.teaching_ocation_id', 'left')
            ->join('fa_order ord', 'ord.id=a.order_id', 'left')
            ->field('a.id,a.name,b.cover,a.order_id,a.create_time,b.name class_name,b.classes_type,b.id classes_id,c.address,b.price amount')
            ->where('a.user_id', $user['uid'])
            ->where('a.status', '1')
            ->where('a.order_id', ">", 0)
            ->where('a.pay_status', 0)
            ->where('a.pay_time', null)
            ->where('b.price', ">", 0)
            ->select();
        //Db::table('eb_apply_form_item')->getlastsql()
        for ($i = 0; $i < count($data); $i++) {
            $data[$i]['cover'] = getImg($data[$i]['cover'], 200, 200);
            if ($user['uid'] == 1) {
                $data[$i]['amount'] = 0.01;
            }
        }
        $this->success('成功', $data, 200);
    }

    public function cancelRegistration(Request $request)
    {
        $user = $this->auth->getUser();

        if (empty($user['uid'])) {
            return $this->error('请先登录', [], 401);
        }

        $params = $request->param();

        if (empty($params)) {
            return $this->error('无相关数据', [], 400);
        }
        $classes_ids = explode(',', $params['classes_id']);
        $result = Db::table('eb_apply_form_item')->where('user_id', $user['uid'])->where('classes_id', 'in', $classes_ids)->update(['status' => 0]);
        //将此次更新的Db::table('eb_apply_form_item')表ID,user_id,classes_id记录到result变量中
        if ($result) {
            $params['classes_id'] = $classes_ids;
            $params['user_id'] = $user['uid'];
            //Db::table('eb_apply_form_item')表ID,多个
            $params['id'] = Db::table('eb_apply_form_item')->where('user_id', $user['uid'])->where('classes_id', 'in', $classes_ids)->column('id');
            $this->success('取消成功', $params, 200);
        } else {
            $this->success('取消失败', $result, 400);
        }
    }

    public function getApplyForm(Request $request)
    {
        $id = $request->param('id');
        if (empty($id)) {
            return $this->error('参数有误', [], 400);
        }
        $result = Db::table('eb_apply_form')->alias('f')
            ->join("fa_course_table table", "f.table_id=table.id", "left")
            ->where('f.school_id', $id)
            ->where('f.status', 1)
            ->where('is_close', 0)
            ->where('is_finished', 0)
            ->where('is_user_end', 0)
            ->where('is_end', 0)
            ->whereRaw("UNIX_TIMESTAMP((SELECT MIN(CONCAT(t2.date, ' ', t2.start_time, ':00')) FROM fa_course_table_autoplan t2 WHERE t2.table_id = table.id AND t2.status=1)) > UNIX_TIMESTAMP(NOW())")
            ->field('f.id,f.table_id,f.apply_type,f.title,f.intro,f.cover,f.start_time,f.end_time,f.max_num,f.is_use_integral,f.dead_time')
            ->group('f.id')
            ->select();
        //$sql=Db::table('eb_apply_form')->getLastSql();
        foreach ($result as $key => $value) {
            $result[$key]['start_time'] = date("Y年m月d日", strtotime($result[$key]['start_time']));
            $result[$key]['end_time'] = date("Y年m月d日", strtotime($result[$key]['end_time']));
            $table = Db::table('fa_course_table')->where('id', $value['table_id'])->find();
            $result[$key]['classes_ids'] = $table['classes_id'];
            $classes_ids = explode(",", $table['classes_id']);
            $result[$key]['max_num'] = Db::table('eb_classes')->where('id', 'in', $classes_ids)->sum('num');
        }
        $this->success('获取成功', $result,
            200);
    }

    public function getNoUpcomingApplyForms(Request $request)
    {
        $id = $request->param('id');

        $query = Db::table('eb_apply_form')->alias('f')
            ->join("fa_course_table table", "f.table_id=table.id", "left")
            ->where('f.status', 1)
            ->where('is_close', 0)
            ->where('is_finished', 0)
            ->where('is_user_end', 0)
            ->where('is_end', 0);

        if (!empty($id)) {
            $query->where('f.school_id', $id);
        }

        $result = $query
            ->whereRaw("NOT EXISTS (SELECT 1 FROM fa_course_table_autoplan t2 WHERE t2.table_id = table.id AND UNIX_TIMESTAMP(CONCAT(t2.date, ' ', t2.start_time, ':00')) > UNIX_TIMESTAMP(NOW()))")
            ->field('f.id,f.table_id,f.apply_type,f.title,f.intro,f.cover,f.start_time,f.end_time,f.max_num,f.is_use_integral,f.dead_time')
            ->select();

        foreach ($result as $key => $value) {
            if (!empty($result[$key]['start_time'])) {
                $result[$key]['start_time'] = date("Y年m月d日", strtotime($result[$key]['start_time']));
            }
            if (!empty($result[$key]['end_time'])) {
                $result[$key]['end_time'] = date("Y年m月d日", strtotime($result[$key]['end_time']));
            }
        }

        $this->success('获取成功', $result, 200);
    }

    public function getTableForm2(Request $request)
    {
        $id = $request->param('id');
        $result = Db::table('eb_apply_form')->where('id', $id)->select();
        $classes_ids = Db::table('fa_course_table')->where('id', $id)->field('classes_id')->find()['classes_id'] ?? '';
        $classes_ids = join(",", explode(",", $classes_ids));
        // 防止SQL注入和空值错误
        if (empty($classes_ids)) {
            $result[0]['max_num'] = 0;
        } else {
            try {
                // 验证classes_ids是否只包含数字和逗号，防止SQL注入
                if (!preg_match('/^[0-9,]+$/', $classes_ids)) {
                    throw new \Exception('无效的班级ID格式');
                }

                $queryResult = Db::query("select sum(num) as max_num from eb_classes where id in (" . $classes_ids . ")");
                $result[0]['max_num'] = isset($queryResult[0]['max_num']) ? $queryResult[0]['max_num'] : 0;
            } catch (\Exception $e) {
                // 记录错误日志
                \think\Log::error('获取班级最大人数失败: ' . $e->getMessage());
                // 设置默认值
                $result[0]['max_num'] = 0;
            }
        }
        $this->success('获取成功', $result,
            200);
    }

    public function getTableFormList(Request $request)
    {
        $result = Db::table('eb_apply_form')
            ->where(function ($query) {
                $query->where(function ($query) {
                    $query->where('f.end_time', 'null');
                })
                    ->orWhere(function ($query) {
                        $query->where('f.end_time', 'not null')
                            ->where('f.end_time', '>', date('Y-m-d H:i:s'));
                    });
            })
            ->field('f.id,table_id,apply_type,title,intro,cover,start_time,end_time,max_num,is_use_integral,dead_time')
            ->select();
        $sql = Db::table('eb_apply_form')->getLastSql();
        foreach ($result as $key => $value) {
            $result[$key]['start_time'] = date("Y年m月d日", strtotime($result[$key]['start_time']));
            $result[$key]['end_time'] = date("Y年m月d日", strtotime($result[$key]['end_time']));
            $table = Db::table('fa_course_table')->where('id', $value['table_id'])->find();
            $result[$key]['classes_ids'] = $table['classes_id'];
            $classes_ids = explode(",", $table['classes_id']);
            $result[$key]['max_num'] = Db::table('eb_classes')->where('id', 'in', $classes_ids)->sum('num');
        }
        $this->success('获取成功', $result,
            200);
    }

    public function getTableForm(Request $request)
    {
        $id = $request->param('id');
        $classes_id = $request->param('classes_id');
        if (empty($id) && empty($classes_id)) {
            return $this->error('参数有误', [], 400);
        }
        if ($id == "undefined") {
            $subQuery = Db::table('fa_course_table_autoplan')
                ->field('MIN(date) as min_date')
                ->where('date', '>', date('Y-m-d H:i:s'))
                ->buildSql();

            $id = Db::name("fa_course_table")
                ->alias("tb")
                ->join([$subQuery => 't1'], 'tb.start_date > t1.min_date', 'left')
                ->order("start_date desc")
                ->where("status", 1)
                ->where("name", "not like", "%社区%")
                ->limit(0, 1)
                ->value("tb.id");
        }
        $result = Db::table('eb_apply_form')->alias('f')
            ->join("fa_course_table table", "f.table_id=table.id", "left")
            ->where('table_id', $id)
            ->where('f.status', 1)
            ->where('is_close', 0)
            ->where('is_finished', 0)
            ->where('is_user_end', 0)
            ->where('is_end', 0);
        if (isset($uid) && $uid > 0) {
            $result = $result->where(function ($query) use ($uid) {
                $query->whereRaw("(
            (
                (f.special_channel_classes IS NOT NULL AND f.special_channel_classes <> '')
                AND FIND_IN_SET(c1.id, REPLACE(REPLACE(REPLACE(COALESCE(f.special_channel_classes, ''), ' ', ','), '\r', ','), '\n', ',')) > 0
                AND (
                    FIND_IN_SET(u.mobile, REPLACE(REPLACE(REPLACE(COALESCE(f.special_channel_phones, ''), ' ', ','), '\r', ','), '\n', ',')) > 0
                    OR FIND_IN_SET(reg.phone, REPLACE(REPLACE(REPLACE(COALESCE(f.special_channel_phones, ''), ' ', ','), '\r', ','), '\n', ',')) > 0
                )
            )
            OR (f.special_channel_classes IS NULL OR f.special_channel_classes = '') and UNIX_TIMESTAMP((
            SELECT MIN(CONCAT(t2.date,' ', t2.start_time, ':00')) 
            FROM fa_course_table_autoplan t2 
            WHERE t2.status=1 AND t2.classes_id=t.classes_id AND t2.table_type=t.table_type)) >=" . time() . ")");
            });
        } else {
            // 未登录用户：只能访问无特殊渠道限制的课程
            $result->where(function ($query) {
                $query->where('f.special_channel_classes', '')
                    ->whereOr('f.special_channel_classes', null);
            });
        }
        $result = $result->field('f.id,table.name table_name,table_id,apply_type,title,intro,cover,`male_age_min`,`male_age_max`,`female_age_min`,`female_age_max`,f.start_time,f.end_time,max_num,is_use_integral,dead_time,f.terms_content,f.applyed_count')
            ->select();
        $sql = Db::table('eb_apply_form')->getLastSql();
        $apply_form = Db::table('eb_apply_form')->where('table_id', $id)->find();
        $applyed_count = Db::table('eb_apply_form_item')->where('table_id', $id)->where('status', 1)->where('pay_status', 1)->group('user_id')->count();
        Db::table('eb_apply_form')->where('id', $id)->update(['applyed_count' => $applyed_count]);
        foreach ($result as $key => $value) {
            $result[$key]['start_time'] = date("Y年m月d日", strtotime($result[$key]['start_time']));
            $result[$key]['end_time'] = date("Y年m月d日", strtotime($result[$key]['end_time']));
        }

        // 防止SQL注入和空值错误
        if ($id == "undefined" || empty($id) || $id == null || $id == 0) {
            $subQuery = Db::table('fa_course_table_autoplan')
                ->field('MIN(date) as min_date')
                ->where('date', '>', date('Y-m-d H:i:s'))
                ->buildSql();

            $id = Db::name("fa_course_table")
                ->alias("tb")
                ->join([$subQuery => 't1'], 'tb.start_date > t1.min_date', 'left')
                ->order("start_date desc")
                ->where("status", 1)
                ->where("name", "not like", "%社区%")
                ->limit(0, 1)
                ->value("tb.id");
        }
        $classes_ids = Db::table('fa_course_table')->where('id', $id)->field('classes_id')->find()['classes_id'] ?? '';
        $classes_ids = join(",", explode(",", $classes_ids));
        if (empty($classes_ids)) {
            $result[0]['max_num'] = 0;
        } else {
            try {
                // 验证classes_ids是否只包含数字和逗号，防止SQL注入
                if (!preg_match('/^[0-9,]+$/', $classes_ids)) {
                    throw new \Exception('无效的班级ID格式');
                }

                $queryResult = Db::query("select sum(num) as max_num from eb_classes where id in (" . $classes_ids . ")");
                $result[0]['max_num'] = isset($queryResult[0]['max_num']) ? $queryResult[0]['max_num'] : 0;
                $result[0]['max_num'] = isset($queryResult[0]['max_num']) ? $queryResult[0]['max_num'] : 0;
            } catch (\Exception $e) {
                // 记录错误日志
                \think\Log::error('获取班级最大人数失败: ' . $e->getMessage());
                // 设置默认值
                $result[0]['max_num'] = 0;
            }
        }
        $result[0]['table_id'] = $id;
        $this->success('获取成功', $result,
            200);
    }

    public function setReadAgree(Request $request)
    {
        $user = $this->auth->getUser();

        if (empty($user['uid'])) {
            return $this->error('请先登录', [], 401);
        }

        $params = $request->param();

        if (empty($params['read_agree'])) {
            return $this->error('参数有误', [], 400);
        }

        $params['updatetime'] = datetime(time());
        Db::name('registration')->where('user_id', $user['uid'])->where('apply_form_id', $params['apply_form_id'])->update($params);

        $this->success('写入成功', $params, 200);

    }

    private function getTableReportByClassesId($classes_id)
    {
        $coursetableQuery = db::name("fa_course_table")
            ->where("classes_id", "like", "%," . $classes_id . ",%")
            ->whereOr("classes_id", "like", "%," . $classes_id)
            ->whereOr("classes_id", "like", $classes_id . ",%")
            ->whereOr("classes_id", "=", $classes_id)
            ->find();
        $table_id = $coursetableQuery["id"];
        $apply_form = db::name("eb_apply_form")->where("table_id", "=", $table_id)->find();
        if ($apply_form) {
            $apply_form_id = $apply_form["id"];
        }
        return [
            "apply_form_id" => $apply_form_id,
            "table_id" => $table_id,
        ];
    }
}